如果我们在类定义本身中定义一个成员函数,它是否必须以内联方式处理,或者它只是对编译器的一个请求,它可以忽略。
答案 0 :(得分:19)
是的,在类体内定义的函数是隐式inline
。
(与声明为inline
的其他函数一样,它并不意味着编译器必须在调用函数的位置执行内联扩展,它只是允许“一个定义规则”的允许松弛,组合要求必须在使用该功能的所有翻译单元中包含定义。)
答案 1 :(得分:5)
正如其他人所说,类内定义的方法是内联自动请求的。 了解原因很有用。
假设不是。您必须为这样的函数生成代码,并且在调用它的任何地方,跳转到子例程指令都必须通过链接器引用该位置。
class A {
public:
void f() { ... your code ... }
};
每次看到此代码时,如果它不是内联的,编译器只能假定它必须生成,因此它会生成一个符号。假设它是这样的:
A__f_v:
如果该符号是全局符号,那么如果您碰巧在不同模块中多次包含此类代码,则在链接时会出现多重定义的符号错误。所以它不可能是全球性的。相反,它是本地文件。
想象一下,您将上述头文件包含在许多模块中。在每一个中,它将生成该代码的本地副本。哪个比完全没有编译好,但是当你真的只需要一个代码时,你会获得多个代码副本。
这导致了以下结论:如果你的编译器不打算内联函数,那么你最好在某处声明它,而不是要求它内联。
不幸的是,内联和不内联是不可移植的。它由编译器编写者定义。一个好的经验法则是总是让每一个衬里,特别是所有自己只是调用函数的函数,内联,当您消除开销时。低于三行线性代码的任何东西几乎都可以。但是如果你在代码中有一个循环,那么问题是编译器是否会允许它内联,更重要的是,即使你做了你想做的事情,你会看到多少好处。
考虑这个内联代码:
inline int add(int a, int b) { return a + b; }
它不仅与源代码中的原型一样小,而且内联代码生成的汇编语言小于对例程的调用。所以这段代码更小,更快。
而且,如果你碰巧传入常数:
int c= add(5,4);
它在编译时解决,没有代码。
在gcc中,我最近注意到即使我没有内联代码,如果它是文件的本地代码,它们也会偷偷地内联它。只有当我在一个单独的源模块中声明该函数时,它们才会优化掉该调用。
另一方面,假设您在1000行代码上请求内联。即使你的编译器足够愚蠢,你唯一能节省的就是调用本身,而且每次调用它时,编译器必须粘贴所有代码。如果你调用该代码n次,你的代码增长了例程* n的大小。所以任何大于10行的东西都不值得内联,除了特殊情况,它只被称为非常少的次数。其中一个例子可能是私有方法,只有另外两个人调用。
如果您请求内联包含循环的方法,那么只有经常执行少量循环才有意义。但考虑一个迭代一百万次的循环。即使代码是内联的,在通话中花费的时间百分比也很小。因此,如果你有方法在其中包含循环,这往往更大,那些值得从头文件中删除,因为它们a)将倾向于被编译器内联拒绝,并且b)即使它们被内联,通常也是如此不会提供任何好处
答案 2 :(得分:3)
编译器必须将其视为内联请求 - 它可以忽略。有一些习惯用于定义标题中的某些函数(例如空虚拟析构函数)和一些必要的标题定义(模板函数),但除此之外,请参阅GotW #33以获取更多信息。
有些人已经注意到编译器甚至可能内联你从未问过的函数,但我不确定这是否会破坏请求内联函数的目的。
答案 3 :(得分:3)
确实内联 - 但编译器可以忽略任何内联请求。
答案 4 :(得分:2)
请求编译器忽略它。
答案 5 :(得分:2)
2003 ISO C ++标准说
7.1.2 / 2带内联的函数声明(8.3.5,9.3,11.4) 说明符声明内联 功能。内联说明符 表示执行情况 内联替换函数 在呼叫点的身体是 优于通常的函数调用
机制。实现不是 需要执行此内联
在通话点替换; 然而,即使这个内联
替换被省略,另一个 内联函数的规则已定义 7.1.2仍应受到尊重。7.1.2 / 3在类定义中定义的函数是内联的 功能。内联说明符应 不出现在块范围函数中 声明。
7.1.2 / 4应在每个翻译单元中定义内联函数 使用和应有的 每一个都有完全相同的定义 案例(3.2)。 [注意:打电话给 可能遇到内联函数 在它的定义出现之前 翻译单位。 ]如果一个功能 声明了外部链接 它在一个翻译单元中内联 应全部在线声明 它所在的翻译单位 出现;无需诊断。一个 内联函数与外部 联系人应具有相同的地址 所有翻译单位。静止的 外部内联中的局部变量
功能总是指相同的 宾语。中的字符串文字 extern内联函数是一样的 不同翻译中的对象
单元。
答案 6 :(得分:1)
有两件事不应该混为一谈: