类内部或外部的函数声明


91

我是一名尝试学习C ++的JAVA开发人员,但我真的不知道标准函数声明的最佳实践是什么。

在课堂里:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

或外面:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

我觉得第二个可能不太可读...


1
实际上,这里有3个选项。您的第二个示例可能在头文件中(但仍未内联)或在单独的.cpp文件中具有函数定义。
科迪·格雷


3
请注意:声明始终在类内部,而定义在内部或外部。问题标题和正文应遵循s / declaration / definition /不相信我?stackoverflow.com/q/1410563/1143274
Evgeni Sergeev

1
必须避免在类内部使用函数定义。它们被隐式地认为inline
John Strood

@JohnStrood是吗?inline仅放宽一个定义规则,如果另一个翻译单位使用则必须这样做Clazz
Caleth

Answers:


57

从某种意义上说,C ++支持软件开发的面向对象范例,因此它是面向对象的。

但是,与Java不同,C ++不会强迫您在类中对函数定义进行分组:声明函数的标准C ++方法是仅声明一个函数,而无需任何类。

相反,如果您正在谈论方法声明/定义,那么标准方法是仅将声明放入包含文件(通常命名为.h.hpp)中,并将定义放入单独的实现文件(通常命名为.cpp.cxx)中。我同意这确实有点烦人并且需要重复,但这是语言的设计方式。

对于快速实验和单个文件项目,任何事情都可以工作...但是对于较大的项目,这种分隔实际上是必需的。

注意:即使您知道Java,C ++还是一种完全不同的语言...,它是无法通过实验学习的语言。原因是这是一种相当复杂的语言,具有很多不对称性和显然不合逻辑的选择,最重要的是,当您犯了一个错误时,没有“运行时错误天使”可以像在Java中一样来拯救您……但是,相反,未定义的行为守护程序”。

学习C ++的唯一合理方法是通过阅读...无论您有多聪明,都无法猜测委员会的决定(实际上,聪明有时甚至是个问题,因为正确的答案是不合逻辑的,是历史的结果。遗产。)

只需挑选一本好书,然后阅读它们即可。


7
如果有人来自Java并寻求C ++的帮助,那么如果您说“您所熟悉的语言迷恋某种东西”,对他有什么影响?他没有与其他语言的比较,所以这几乎没有告诉他。最好不要使用像痴迷这样的带有情感意味的简单单词,该单词并不能告诉OP太多,您可以考虑将这一部分省略掉。此外,“每时每刻使用一个类”的上下文是什么?在Java中,请勿将类用于方法。您不要将类用于变量。您不使用文件类。.那么,这里的“一切”是什么?ant?
Daniel S.

3
@DanielS:删除了该部分,因为显然冒犯了您(不知道为什么)。可以肯定的是,我根本不使用Java,因为我根本没有使用Java,当时我只是以为OOP作为对象痴迷编程是一个有趣的笑话,而显然不是。我曾经是Java 1.1认证的程序员,但当时我决定,除非出于某种原因而被迫放弃,否则我不会使用该“编程语言”,到目前为止,我已经成功地避免了使用它。
6502

谢谢,我认为它现在读起来要好得多。抱歉,我听起来很生气。下次我会尝试变得更加积极。
Daniel S.

15
没有回答问题
Petr Peller

1
@PetrPeller:第三段中您不清楚的部分是什么?
6502年

27

第一个将您的成员函数定义为内联函数,而第二个则没有。在这种情况下,函数的定义位于标头本身中。

第二种实现是将函数的定义放在cpp文件中。

两者在语义上都不同,而不仅仅是样式问题。


2
cplusplus.com/doc/tutorial/classes给出了相同的答案:“完全在其类中定义类成员函数或仅包括原型以及其后定义之间的唯一区别是,在第一种情况下,该函数将自动被编译器认为是内联成员函数,而第二个将是普通的(非内联)类成员函数,实际上假定行为没有差异。”
Buttons840

18

函数定义在类之外更好。这样,如果需要,您的代码可以保持安全。头文件应该只给出声明。

假设有人想要使用您的代码,则可以给他类的.h文件和.obj文件(编译后获得)。他不需要.cpp文件来使用您的代码。

这样,您的实现对其他任何人都不可见。


10

“类内”(I)方法的作用与“类外”(O)方法相同。

但是,当仅在一个文件(.cpp文件内部)中使用类时,可以使用(I)。(O)在头文件中时使用。cpp文件始终被编译。使用#include“ header.h”时,将编译头文件。

如果在头文件中使用(I),则每次包含#include“ header.h”时都会声明函数(Fun1)。这可能导致多次声明相同的功能。这很难编译,甚至可能导致错误。

正确用法示例:

文件1:“ Clazz.h”

//This file sets up the class with a prototype body. 

class Clazz
{
public:
    void Fun1();//This is a Fun1 Prototype. 
};

File2:“ Clazz.cpp”

#include "Clazz.h" 
//this file gives Fun1() (prototyped in the header) a body once.

void Clazz::Fun1()
{
    //Do stuff...
}

File3:“ UseClazz.cpp”

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.

File4:“ AlsoUseClazz.cpp”

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header. 

File5:“ DoNotUseClazzHeader.cpp”

//here we do not include Clazz.h. So this is another scope. 
class Clazz
{
public:
    void Fun1()
    {
         //Do something else...
    }
};

class MyClazz; //this is a totally different thing. 
MyClazz.Fun1(); //this does something else. 

你是说Clazz MyClazzClazz MyClazz2
Chupo_cro

4

成员函数可以在类定义中定义,也可以使用范围解析运算符::单独定义。即使您不使用内联说明符,在类定义中定义成员函数也会声明该内联函数。因此,您可以如下定义Volume()函数:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

     double getVolume(void)
     {
        return length * breadth * height;
     }
};

如果愿意,可以使用范围解析运算符::如下定义类外的相同函数:

double Box::getVolume(void)
{
   return length * breadth * height;
}

在这里,唯一重要的一点是,您必须在::运算符之前使用类名。将在对象上使用点运算符(。)调用成员函数,该成员函数将仅按以下方式操纵与该对象有关的数据:

Box myBox;           

myBox.getVolume();  

(摘自:http : //www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm),这两种方法都是合法的。

我不是专家,但是我认为,如果您仅将一个类定义放在一个文件中,那么这并不重要。

但是如果您应用内部类之类的东西,或者您有多个类定义,那么第二个将很难阅读和维护。


1
您能否将链接中的相关内容带入您的帖子正文中,从而防止将来出现的死链接?谢谢
JustinJDavies 2014年

2

第一个必须放在头文件中(类的声明所在的位置)。第二个可以在任何位置,无论是标头还是通常是源文件。在实践中,您可以将小函数放在类声明中(该类函数隐式地内联声明它们,尽管最终由编译器决定是否将其内联)。但是,大多数函数在标头中都有声明,而在cpp文件中有实现,就像第二个示例中一样。不,我没有看到任何原因使得它的可读性较差。更不用说您实际上可以将实现的类型拆分为多个cpp文件。


1

在类内部定义的函数默认情况下被视为内联函数。您应该在外部定义函数的简单原因:

该类的构造函数检查虚函数,并初始化一个虚拟指针以指向正确的VTABLE或虚拟方法表,调用基类构造函数,并初始化当前类的变量,因此它实际上做了一些工作。

内联函数在功能不太复杂时使用,并避免了函数调用的开销。(开销包括硬件级别的跳转和分支。)如上所述,构造函数并不像内联那样简单。


“内联”实际上与内联无关。内联定义的成员函数被隐式声明为内联的事实是为了避免违反ODR。
大温度

0

内联函数(当您在类中声明它们时的函数)每次调用时都会将它们粘贴到您的主内存代码中。当您在类之外声明函数时,调用函数时它来自同一内存。那就是为什么它更好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.