头中定义的C++类方法是否总是内联的

Are C++ class methods defined in the header always inlined?

本文关键字:是否 类方法 定义 C++      更新时间:2023-10-16

编辑:我已经恢复了原始标题,但我真正应该问的是:"C++链接器如何处理在多个对象文件中定义的类方法">

假设我在标题中定义了一个C++类,如下所示:

class Klass
{
    int Obnoxiously_Large_Method()
    {
        //many thousands of lines of code here
    }
}

如果我编译了一些在多个位置使用"Obnoxiously_Large_Method"的C++代码,那么生成的对象文件是否总是内联"Obnoxiously_LTarget_Method'"的代码,或者它是否会优化大小(例如,当使用g++-Os时(,并创建"Obnoxiously_Ltarget_Method"的单个实例,并像使用普通函数一样使用它?,如果是,链接器如何解决实例化了相同函数的其他对象文件之间的冲突?。是否有一些神秘的C++命名空间Juju可以防止方法的单独对象实例相互冲突?

7.1.2功能说明符

带有内联说明符的函数声明(8.3.5、9.3、11.4(声明了一个内联函数。内联说明符指示在调用点优先于通常的函数调用机制。执行此内联替换不需要实现在呼叫点;但是,即使此内联替换省略,7.1.2定义的内联函数的其他规则应仍然受到尊重。

因此,编译器实际上不需要"内联"任何函数。

然而,标准还说,

具有外部链接的内联函数在所有翻译单元中应具有相同的地址。

成员函数通常具有外部链接(一个例外是当成员函数属于"local"类时(,因此内联函数必须有一个唯一的地址,以用于获取函数地址的情况。在这种情况下,编译器将安排链接器丢弃该函数的非内联副本的除一个实例外的所有实例,并将对该函数的所有地址引用修复为保留的地址引用。

C++98标准第[9.3]节"成员函数"规定:

成员函数可以在其类定义中定义(8.4(,在这种情况下,它是内联成员函数(7.1.2(

因此,一直存在这样的情况,即明确地标记在类定义中定义的成员函数inline是不必要的。

inline函数说明符上,标准规定:

带有inline说明符的函数声明(8.3.5、9.3、11.4(声明了内联函数。内联说明符向[C++编译器]指示,与通常的函数调用机制相比,在调用点对函数体的内联替换更可取。[然而,C++编译器]不需要在调用点执行此内联替换;

因此,编译器是否会真正内联函数的定义,而不是通过通常的函数调用机制来调用它,这取决于编译器。

没有什么总是内联的(除非你的编译器有一个属性或私有关键字来强制它这样做……此时你正在编写$(compiler(风格的C++,而不是标准C++(。超长函数、递归函数和其他一些东西通常不会内联。

如果编译器确定这样做会降低性能、不合理地增加对象文件的大小或使事情无法正常工作,那么它可以选择不内联内容。或者它是在优化尺寸而不是速度。或者你要求它不要,或者它不喜欢你的衬衫。或者,如果它今天感觉很懒,那是因为它昨晚汇编得太多了。或者出于任何其他原因。或者根本没有理由。

这个问题没有单一的答案。编译者是聪明的野兽。如果需要,可以专门使用内联单词,但这并不意味着编译器会实际内联函数。

内联可以帮助开发人员进行优化。它向编译器暗示某些东西应该内联,但这些提示现在通常被忽略,因为编译器可以在寄存器分配和决定何时内联函数方面做得更好(事实上,编译器可以在不同的时间内联或不内联函数(。现代处理器上的代码生成要比里奇发明C.时常见的更具确定性的处理器上复杂得多

这个词现在在C++中的意思是,它可以有多个相同的定义,并且需要在每个使用它的翻译单元中进行定义。

也就是说,我曾经和一个greenhills编译器一起工作,它实际上服从了我的意愿,而不是违背了它:(。。这真的取决于编译器。

inline关键字处理函数的c++定义。编译器可以在任何需要的地方内联对象代码。

内联定义的函数(例如,它们使用inline关键字(,在每个编译单元中为函数创建对象代码。这些函数被标记为特殊函数,因此链接器知道只使用一个。

有关更多细节,请参阅此答案。

它不必内联,否;这就像您明确指定inline一样。

当您编写inline时,您承诺不会从未定义该方法的翻译单元调用该方法,因此,它可以具有内部链接(因此链接器不会将一个对象文件对它的引用连接到另一个对象档案对它的定义([这段话错了。我把它原封不动,只是删掉了,这样下面的评论仍然有意义。]