用C ++在头文件中编写函数定义
我有一个有许多小function的课程。 通过小函数,我的意思是函数不做任何处理,只是返回一个文字值。 就像是:
string Foo::method() const{ return "A"; }
我创build了一个头文件“Foo.h”和源文件“Foo.cpp”。 但是由于函数非常小,我正在考虑把它放在头文件本身。 我有以下问题:
- 如果我把这些函数定义在头文件中,是否有任何性能或其他问题? 我会有很多这样的function。
- 我的理解是,编译完成后,编译器将扩展头文件并将其放在包含它的位置。 那是对的吗?
如果函数很小(可能会改变它的频率很低),并且如果函数可以放入头文件中而不包含大量其他头文件(因为函数依赖于它们),这样做是完全有效的。 如果您将它们声明为extern inline,则编译器需要为每个编译单元提供相同的地址:
headera.h :
inline string method() { return something; }
成员函数是隐式内联的,只要它们在类中定义。 同样的事情对他们来说也是如此:如果他们可以毫无麻烦地被放入标题,你确实可以这样做。
由于函数的代码放在头文件中并且是可见的,因此编译器可以内联调用它们,也就是将函数的代码直接放在调用位置(不是因为在内部放置了内联,而是更多因为编译器是这样决定的,只是把内联放到编译器中就可以了)。 这可能会导致性能改进,因为编译器现在可以看到参数与函数本地的variables匹配,而参数不会互相混淆 – 最后但并非最不重要的是,函数帧分配不再需要了。
我的理解是,编译完成后,编译器将扩展头文件并将其放在包含它的位置。 那是对的吗?
对,那是正确的。 该函数将在您包含标题的每个地方定义。 编译器会关心只把它的一个实例放到生成的程序中,通过消除其他的。
根据您的编译器和它的设置,它可以执行以下任何操作:
- 它可能会忽略inline关键字(这只是提示编译器,而不是命令)并生成独立的函数。 如果你的函数超过了依赖于编译器的复杂度阈值,它可能会这样做。 例如太多的嵌套循环。
- 它可能会决定你的独立function是一个很好的内联扩展的候选人。
在很多情况下,编译器在确定一个函数是否应该内联比你更好,所以没有必要再次猜测它。 当一个类有许多小函数时,我喜欢使用隐式内联,因为在类中有实现的方便。 对于较大的function来说,这并不适用。
另外要记住的是,如果你在一个DLL /共享库中导出一个类(不是一个好主意恕我直言,但是人们无论如何都行),你需要非常小心使用内联函数。 如果构build该DLL的编译器决定一个函数应该被内联,那么你有几个潜在的问题:
- 使用DLL构build程序的编译器可能会决定不内联该函数,以便它将生成符号引用,该函数不存在,并且不会加载该DLL。
- 如果您更新DLL并更改内联函数,客户端程序将仍然使用该函数的旧版本,因为该函数已内联到客户端代码中。
性能会有所提高,因为头文件中的实现是隐式内联的。 正如你所说的你的function很小,内联操作将是如此有益于你恕我直言。
关于编译器的说法也是正确的。编译器(除了内联)在头文件或.cpp
文件中的代码之间没有区别。
-
如果你的function很简单,把它们放在内联中,你就必须把它们放在头文件中。 除此之外,任何公约就是这样 – 公约。
-
是的,编译器确实扩展了遇到#include语句的头文件。
这取决于适用于您的案例的编码标准,但是:
没有循环和其他任何小函数应该内联以获得更好的性能(但稍大的代码 – 对于一些受限或embedded式应用程序来说很重要)。
如果你在头文件中有这个函数的主体,你会默认使用inline(d)(这对于速度来说是件好事)。
在编译器创build目标文件之前,调用预处理器(gcc的-E选项),并将结果发送到编译器,该编译器创build代码之外的对象。
所以较短的答案是:
– 在头文件中声明函数有利于速度(但不是空间) –
你应该使用内联函数。 阅读这些内联函数以获得更好的理解和权衡。
C ++不会抱怨,但一般来说,你不应该这样做。
当你包含一个文件的时候,包含文件的全部内容就会被包含在内。 这意味着您将放入标题的任何定义都复制到包含该标题的每个文件中。
对于小型项目来说,这不太可能是个问题。 但是对于大型项目来说,这可能会让编译时间更长(因为每次遇到相同的代码都会重新编译),并且可能会大大增加可执行文件的大小。 如果您对代码文件中的定义进行了更改,则只需要重新编译该.cpp文件。 如果您对头文件中的定义进行更改,则需要重新编译包含头文件的每个代码文件。 一个小小的变化可能会导致您必须重新编译整个项目!
有时例外是为了不太可能改变的微不足道的函数(例如函数定义是一行)。