在类内或外声明函数

107

我是一位JAVA开发者,正在尝试学习C ++,但我不太清楚标准函数声明的最佳实践。

在类中:

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

或者在外面:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}
我有一种感觉第二个可能不太易读...

1
这里实际上有三个选项。你的第二个例子可以在头文件中定义函数(但仍然不是内联的),或者在一个单独的.cpp文件中定义。 - Cody Gray
1
这个问题可能会帮助你理解:https://dev59.com/NHRC5IYBdhLWcg3wVvYt - Björn Pollex
3
注意:声明(declaration)始终位于类内部,但定义(definition)可以在内部或外部。问题标题和正文应受到s/declaration/definition/的影响。不相信我?请看https://dev59.com/V3M_5IYBdhLWcg3wXx1N - Evgeni Sergeev
1
类内部应避免函数定义。它们被视为隐式的“内联”。 - John Strood
@JohnStrood 那又怎样?inline只是放宽了一个定义规则,如果另一个翻译单元使用Clazz,这是必要的。 - Caleth
8个回答

64
C++是面向对象的,它支持面向对象的软件开发范式。
然而,与Java不同,C++不强制将函数定义分组到类中:声明函数的标准C++方式是只声明函数,而不包含任何类。
如果你说的是方法的声明/定义,那么标准的做法是将声明放在一个包含文件中(通常命名为.h或.hpp),将定义放在一个单独的实现文件中(通常命名为.cpp或.cxx)。我同意这确实有些烦人,并且需要一些重复,但这是语言的设计方式(主要概念是C++编译是逐个单元完成的:你需要正在编译的单元的.cpp文件和被编译代码使用的所有.h文件;换句话说,类的包含文件必须包含生成使用该类的代码所需的所有信息)。关于这个问题有很多细节,涉及到编译速度、执行速度、二进制大小和二进制兼容性等不同的影响。
对于快速实验,任何方法都可以...但对于更大的项目,分离是实际上所需的(即使在公共的.h文件中保留一些实现细节也是有意义的)。
注意:即使你懂Java,C++是一门完全不同的语言...而且这是一门不能仅凭实验来学习的语言。原因是它是一门相当复杂的语言,有很多不对称和看似不合逻辑的选择,最重要的是,当你犯错时,没有像Java那样的“运行时错误天使”来拯救你...而是有“未定义行为恶魔”。
学习C++的唯一合理方法是阅读...无论你有多聪明,你都无法猜测委员会的决定(实际上,聪明有时甚至是个问题,因为正确答案是不合逻辑的,是历史遗产的结果)。
只需选择一本或两本好书,并通读它们,再加上实验。仅凭实验是无法达到熟练掌握C++的水平的。

10
如果有人从Java转到C++并请求帮助,如果你说“你熟悉的语言过于偏执”,对他来说这告诉他几乎没有什么意义,因为他无法将其与其他语言进行比较。最好不要使用像“偏执”这样情感色彩强烈的词汇,因为这不会对提问者有太大的帮助,而是可以考虑省略该部分。此外,“用类来代替所有东西”的上下文是什么?在Java中,你不会为一个方法使用一个类,也不会为一个变量或一个文件使用一个类……那么这里的“所有东西”指的是什么?是发牢骚吗? - Daniel S.
3
@DanielS:由于显然冒犯了你(我不知道为什么),所以我删除了那部分内容。当时,我只是觉得OOP被解释为“Object Obsessed Programming”是一个有趣的笑话,但显然它并不好笑。我曾经是Java 1.1认证程序员,但当时决定除非被迫,否则不会使用这种“编程语言”,到目前为止,我成功地避免了使用它。 - 6502
27
不回答问题。 - Petr Peller
2
@PetrPeller:第三段有什么不清楚的地方吗? - 6502
1
第三段有什么不清楚的地方吗?虽然我知道这个答案现在已经快10年了,但是a)它远远超过了这里的其他答案,b)你谈论了很多关于标准做事情的方式,并提到将声明和定义分离是“实际上必需的”,但完全没有谈论两种方法之间的实际区别,而这正是问题最初的问法。 - HotelCalifornia
显示剩余2条评论

32

第一个定义将你的成员函数作为一个内联函数,而第二个则不是。在这种情况下,函数的定义位于头文件中。

第二个实现会将函数的定义放置在cpp文件中。

两者在语义上是不同的,并不仅仅是风格问题。


2
http://www.cplusplus.com/doc/tutorial/classes/ 给出了相同的答案:“在类内完全定义一个类成员函数或者只包含原型并稍后定义它之间唯一的区别是,在第一种情况下,编译器会自动将该函数视为内联成员函数,而在第二种情况下,它将是一个普通(非内联)类成员函数,实际上在行为上没有任何区别。” - Buttons840

22

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

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

这样,您的实现对其他人就不可见了。


14

“内部类”方法(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. 
};

文件2: "Clazz.cpp"

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

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

文件3:"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.

文件4:"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. 

文件5: "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. 

1
你是指 Clazz MyClazzClazz MyClazz2 吗? - Chupo_cro

4

成员函数可以在类定义内部或使用作用域解析运算符 :: 单独定义。在类定义内部定义成员函数声明该函数为内联函数,即使您不使用 inline 说明符也是如此。因此,您可以按以下方式定义 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

2

第一个函数必须放在头文件中(类声明所在的位置)。第二个可以放在任何地方,无论是头文件还是通常情况下是源代码文件。在实践中,您可以将小型函数放在类声明中(这样会隐式地将它们声明为内联函数,但最终是否将其内联取决于编译器)。然而,大多数函数在头文件中声明,在cpp文件中实现,就像您第二个示例中所示。不过,我看不出为什么这样做会导致可读性降低。更不用说您实际上可以将类型的实现分散到几个cpp文件中。


1

在类内定义的函数默认被视为内联函数。以下是定义函数外部的一个简单原因:

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

内联函数用于函数不太复杂且避免函数调用开销。(开销包括硬件级别上的跳转和分支。)正如上面所述,构造函数不像内联函数那么简单。


“inline” 实际上与内联几乎没有关系。在线定义的成员函数被隐式声明为内联是为了避免 ODR 违规。 - Big Temp

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接