将内联方法从头文件移动到.cpp文件

时间:2013-06-11 21:44:12

标签: c++ class methods

我在foo.h头文件

中定义了以下类
class Foo {

public:
    inline int Method();

};

inline int Foo::Method() { // Implementation }

我现在想将实现移到foo.cpp文件中。为此,我必须删除inline关键字并将方法的实现移至foo.cpp这样的文件

#include `foo.h`

inline int Foo::Method() { // Implementation }

我有两个问题:

  1. 我关于删除inline关键字的说法是否正确?是否必须删除?
  2. 删除inline关键字对性能的影响通常如此(几乎所有方法都已内联)?
  3. 非常感谢你。

6 个答案:

答案 0 :(得分:15)

如果您将函数定义从标题移动到cpp文件,则必须删除该函数的所有所有位置的inline关键字。对于较旧的链接器,它可能会使事情稍微变慢,但是对于现代链接器,应该注意到性能没有真正的差异。 *

在某些情况下,公共成员函数可以是inline,但这只是个坏主意。不要这样做。可以将某些私有成员函数标记为inline,但实际上您希望__attribute__((always_inline))__forceinline

*在极少数情况下,它会有所作为,但99%的时间不会,99.9%的你不在乎。如果测量显示您达到了千分之一,那么您可以使用前面提到的__forceinline

答案 1 :(得分:5)

关键字inline在课堂上是多余的。如果你有一个功能体,就暗示了这一点。

在实施文件中,它也相当多余。

它的唯一用途是,如果您在标题(或类外的成员函数,但在标题中)中定义一个自由函数,以避免多个实体。

在雾现代编译器上进行优化,它更加冗余,无论如何都会毫无疑问地内联任何内容,或者随意忽略您的关键字。

内联使用必须一致!从7.1.2p4开始:

  

内联函数应在每个使用过的翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2)。 [注意:在定义出现在翻译单元之前,可能会遇到对内联函数的调用。 -end note]如果函数的定义在第一次声明为内联之前出现在翻译单元中,则程序格式错误。如果是具有外部链接的功能   在一个翻译单元中内联声明,应在其出现的所有翻译单元中内联声明;无需诊断。 ...

答案 2 :(得分:4)

如果函数不是TINY(或者有多个参数,但没有做太多,比如构造函数或类似的东西,需要很多东西,只是将它复制到类中的某个地方),内联它首先对性能影响不大。 Setter和getter通常是内联的良好候选者,因为他们(通常)只是将数据从一个地方复制到另一个地方,并且可以在发生呼叫的地方轻松完成。

正如其他人所说,这是一个“请编译好,如果我可以请求你,请考虑内联这个功能” - 它不是“使这个函数内联”。另一方面,编译器通常会内联函数,而不管是否存在inline关键字。它查看函数的大小,调用的数量以及代码从内联获得的大小。

如果将函数移动到“foo.cpp”,它将仅在“foo.cpp”编译单元内部(通常是compile unit = source file)内联。

除非你有一个能够“完整程序优化”或类似技巧的编译器,并且启用该功能 - 这基本上意味着编译器不会生成带有机器代码的准备链接目标文件,而是生成“已解析的,但没有完全翻译成机器指令“目标文件。然后,当最终将可执行文件(或共享库)放在一起时,编译器/链接器将从“中途”代码生成一大块机器代码。 MS和GCC都支持这一点,但我不知道它对大型项目的效果如何。

编辑:

根据Mooing Duck的评论:inline函数不会在目标文件中生成真正的函数名称,因此链接器也可能会为unresolved symbol int Foo::Method() [或某种程度的措辞]提供错误。

结束编辑。

如果性能至关重要,则应测量当前代码的性能,然后进行更改,并再次进行测量。如果它有显着差异,你会得到答案。如果它更快(因为内联较少导致其他代码位的缓存命中率更高),那么这很好。如果速度较慢,则必须将(部分)函数放回头文件中。或者让它变得更慢......或者找到一些让它再次变得更快的其他方式...选择是你的(并且,如果你在一个小组中工作,其他人可能在最终决定中有发言权,当然)。对于任何人来说,几乎不可能在没有理解整个程序架构以及类中发生的事情的情况下以哪种方式说出来 - 在帖子中名称“foo.cpp”可能不是真实的代码......

答案 3 :(得分:4)

你和这里提供小功能建议的人正在以inline老式的方式看待。

inline过去的意思是“我希望这段代码能够快速运行,所以每当我调用这个函数时,我都希望你就地扩展它以避免函数调用的开销。”

这是一个非常好的优化。事实上,即使你没有指定inline,编译器也会急切地这样做。

编译器也可以扩展您的inline函数。所以你真的不必担心它会如何影响性能,因为如果你以愚蠢的方式使用它,编译器可以而且会忽略inline

事实上,今天的编译器几乎始终忽略了您对inline的使用,并且只做他们认为最好的事情。

所以,知道这一点,为什么人们仍然使用inline

现在使用inline只有一个原因,那就是解决一个定义规则(ODR)。

在C / C ++中,您只能定义一次函数。如果你这样做:

int foo() { /* do something */ }
int foo() { /* do something else */ }

编译器会抱怨你已经两次定义了相同的函数。

这看起来像一个愚蠢的例子,但是当你使用#include时,做这样的事情特别容易 - 如果你在标题中定义了你的函数,并且#include两次使用相同的标题,这正是你正在做的事情。

值得庆幸的是,inline还有一个用途,它今天仍然有效:如果你将一个函数标记为inline,它会强制编译器使ODR问题无效,从而可以在头文件中定义你的函数

换句话说,inline现在意味着“我想在标题中定义此函数。”

当您以这种方式查看时,应该清楚的是,在将函数移动到cpp文件时应删除inline


为了感兴趣,有几个地方隐含地内联函数。其中一个是班级成员职能:

struct Foo {
    void bar() { /* do something */ }
};

我见过人们标记像inline这样的函数,但这完全是多余的。无论如何编译器都会这样做;没有必要担心ODR,也没有任何表现可以获得。

另一个地方是模板。由于模板已在标题中定义,因此它们不受ODR限制,inline它们是多余的。

答案 4 :(得分:1)

这可能令人困惑,但您不应该想到inline使编译器内联函数的目的。 (现代编译器在内联函数或内联函数方面比你更聪明。)

不,inline的真正目的是告诉链接器不要担心函数的多个定义。如果将(非成员)函数的定义放在标头中,则应将其标记为inline以避免链接器错误。

答案 5 :(得分:0)

2. How typically the removal of the inline keyword affect the performance (practically all my methods are inlined)?

inline关键字告诉编译器获取该函数的实现代码并将其放在函数调用的位置。这减少了堆栈上的函数调用次数,如果使用得当,可以提高程序的性能。

inline关键字只能用于小功能。 Get和Set函数就是很好的例子。它们设置一个变量的值或返回一个变量的值。

如果使用大量代码inline创建一个函数,它可能会大大增加代码的大小(取决于函数代码的大小和使用函数的次数)降低程序的性能。