标题中定义的C ++类方法是否始终内联?

时间:2011-12-05 22:37:35

标签: c++ header linker

  

编辑:我已经恢复了原始标题,但实际上我应该问的是:'C ++链接器如何处理已在多个目标文件中定义的类方法'

假设我在这些行的标题中定义了一个C ++类:

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

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

6 个答案:

答案 0 :(得分:11)

  

7.1.2函数说明符

     

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

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

然而,标准也说,

  

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

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

答案 1 :(得分:4)

总是内联的内容(除非您的编译器有一个属性或私有关键字强制它这样做...此时您正在编写$(COMPILER) - 有趣的C ++而非标准C ++)。很长的函数,递归函数和其他一些东西通常都没有内联。

如果确定这样做会降低性能,不合理地增加目标文件的大小或使工作不正确,编译器可以选择不内联。或者,如果它是针对大小而不是速度进行优化的。或者,如果你不这样做。或者如果它不喜欢你的衬衫。或者,如果它今天感觉很懒,导致它昨晚编译得太多了。或者出于任何其他原因。或者完全没有理由。

答案 2 :(得分:4)

C ++ 98标准的第[9.3]节,成员函数:

  

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

因此,在类定义中明确inline定义的标记成员函数一直是不必要的。

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

  

带有inline说明符的函数声明(8.3.5,9.3,11.4)声明了内联函数。内联说明符向[C ++编译器]指示在调用点处函数体的内联替换优先于通常的函数调用机制。 [但是,C ++编译器]不需要在调用点执行此内联替换;

因此,编译器是否实际内联函数的定义而不是通过通常的函数调用机制来调用它。

答案 3 :(得分:2)

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

Inline可以帮助开发人员进行优化。它暗示了编译器应该内联的东西,但是这些提示现在通常被忽略,因为编译器可以在寄存器赋值方面做得更好并决定何时内联函数(实际上,编译器可以内联或不在不同时间内联函数) )。现代处理器上的代码生成远比Ritchie发明C时更常见的更复杂。

现在,在C ++中,这个词的意思是它可以有多个相同的定义,并且需要在每个使用它的翻译单元中定义。 (换句话说,你需要确保它可以内联。)你可以在标题中有一个没有问题的内联函数,并且类定义中定义的成员函数会自动有效地内联。

那就是说,我曾经使用过greenhills编译器,它实际上听从了我的意愿而不是它不服从它:) ..这取决于编译器,真的。

答案 4 :(得分:1)

不必内联,不;就像你明确指定inline一样。

当你写inline时,你保证不会从没有定义它的翻译单元调用这个方法,因此它可以有internal linkage(所以链接器不会将一个对象文件的引用连接到另一个对象文件的定义。 [此段落错误。我将它保持原样,只是删掉了,所以下面的评论仍然有意义。]

答案 5 :(得分:1)

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

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

有关详细信息,请参阅this answer