繁体   English   中英

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

[英]Moving inline methods from a header file to a .cpp files

我在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关键字通常会影响性能(实际上我的所有方法都是内联的)?

非常感谢你提前。

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

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

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

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

在实现文件中,它也是相当多余的。

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

雾现代编译器的优化方面更加冗余,他们无论如何都可以毫无疑问地内联任何内容,或者随意忽略您的关键字。

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

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

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

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

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

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

编辑:

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

结束编辑。

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

你和这里的人们提供有关小功能的建议,正在以老式的方式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它们是多余的。

它可能会令人困惑,但你不应该想到inline的目的是使编译器内联一个函数。 (现代编译器在内联函数或无论何时内联时都比你聪明得多)。

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM