有没有办法防止标头定义的c ++函数被视为内联

时间:2009-12-07 17:45:55

标签: c++ qt header inline

我正在制作一个Qt应用程序,当我编写代码时,我习惯于在标题中定义我的插槽。我发现虽然我仍然在.cpp中定义了正常的函数,但我更容易开发这种方式(除非函数非常小)。但是现在我的同事有一些担心,将这些放在标题中是不好的做法,因为在标题中定义它们的事实使它们内联,所以我正在调查此事以理解正在发生的一切。这就是我给出的原因:

“即使是内联函数(除了类所要求的)也是一个值得商榷的实践。理论上,它创建了更快但更大的代码(通过复制代码避免了函数调用和返回)。但是,有几个人注意到了通常使用in-lining实际上会创建更慢的代码。原因是它可能导致代码变大并超出适合运行时使用的一个或多个缓存的大小。因此它会导致部分每次通过某个循环进入和退出缓存的函数,缓存未命中和后续重新加载比对另一个缓存页中的某些东西的函数调用要昂贵得多。这是一个有趣的情况,一个无法预测,只有通过反复试验来观察。“

10 个答案:

答案 0 :(得分:24)

您的同事需要检查C ++中内联的含义。

这个词有两个含义,重要的是要将它们分开:

根据C ++标准,如果函数用inline关键字标记,或者在类定义中定义,则函数为inline

唯一需要的效果是禁用一个定义规则 - 也就是说,使定义在多个转换单元中看到是合法的,而不会产生链接器错误。基本上,它允许您将完整定义放在头文件

然后是“内联”优化,它包括获取函数体,并插入而不是函数调用。

这些含义几乎完全正交。无论程序员是否将其标记为inline,编译器都可以内联函数。 (虽然如果函数在与定义函数的转换单元不同的转换单元中调用,编译器内联更难且更不常见。)C ++中标记为inline的函数可能是也可能不是由编译器内联。编译器尝试根据代码大小,调用函数的频率,调用站点的数量和此类启发式来估计此可能的好处。结果是编译器非常擅长确定内联优化何时值得,并且最好的选择通常是让它独自完成它。

您应该简单地将函数标记为内联,1)方便您,2)您希望确保编译器具有应用内联优化的选项

但是你并没有强迫编译器内联任何东西。您只是安排代码,以便它可以选择这样做,内联函数调用。

答案 1 :(得分:6)

由于这些方法是插槽,因此不会内联它们。 根据定义,插槽由指向函数的指针调用。 即使它们被明确声明为 inline , 编译器必须为它们生成正常的函数代码, 所以他们的指针可以被拿走。

唯一的问题(如果你这样看)是由于引起的编译时间较长 在包含类定义的所有编译单元中重新编译相同的方法 然后在链接时删除重复版本。


正如其他人所说,C ++中的 inline 并不意味着编译器会内联函数。 反过来说:缺少内联不会停止强烈优化编译器 和链接器在他们认为它会加速程序时内联一些函数。

C ++中的

内联只对编译器说了一件事: 此函数在头文件中定义,因此可以单独编译 在多个编译单元中。 这不是错误,所以不要发出任何错误或警告消息。 相反,在合并期间,应该处理此功能的多个版本 作为一个。 编译器甚至没有义务检查这些多个版本是否一致。

答案 2 :(得分:2)

正如其他人已经指出的那样,编译器可以自由地执行,如果函数很大,很可能不会内联它们。

但是,还有其他原因可能导致头文件中不需要太多代码。一个是编译时间;如果标头包含在许多不同的.cpp文件中,那么你将为编译器提供更多解析。

另一个原因可能是,如果您或您的雇主打算将代码作为封闭源库分发,您将公开部分实现。

所以你必须权衡利弊,但内联不是缺点之一。

答案 3 :(得分:1)

Visual Studio支持

__declspec(noinline)

请参阅http://msdn.microsoft.com/en-us/library/kxybs02x%28VS.80%29.aspx

可能其他编译器也有类似的构造。

答案 4 :(得分:1)

对我来说,在H或CPP文件中实现天气的主要原因取决于H文件的公共性。 如果H文件用于定义与其他模块的公共接口(通常意味着相应的CPP文件不与包含H文件的文件一起编译),我想在H中包含尽可能少的实现,并且实施CPP中的所有内容。

答案 5 :(得分:1)

如果函数是类成员,则将该函数声明为虚拟函数。

答案 6 :(得分:0)

在标题中定义函数为编译器选择使它们内联。如果它认为结果会更有效,它会这样做,这可能会根据您是否要求最快或最小的代码而改变。该函数可能在多个目标文件中定义,但链接器应该检测到并消除重复项。

答案 7 :(得分:0)

接受的答案在所说的内容中是正确的,但却完全无关紧要。您关注的是代码的实际内联,是的,除非您采取具体措施来避免这种情况,否则将会在标题中执行所有操作。

然而,这几乎总是你想要的。对于你来说,这种情况通常不会太难以维持。也就是说,它们的文字太多了,你无疑会把它们分开。此外,对于模板,它可能是一个问题,因为你需要记住它可能会产生许多实际的方法,并且假设它知道它能够和不能丢弃是不明智的,因为通常它不会。

答案 8 :(得分:0)

不编写内联代码的原因是任何在动态库中外部可见的类。

  • 如果代码更改,内联代码会强制重新编译所有库用户。
  • 使用导出的C函数和最小的C头将C代码链接到C ++库变得不可能,因为最终结果不包含内联代码。
  • 还有一些关于typeid信息的混乱,我不记得所有细节。

答案 9 :(得分:-1)

回应这个:

  

甚至内联功能(除了   按照课程的要求)是一个很高的   有争议的做法。从理论上讲,它   创建更快但更大的代码   (避免函数调用和返回   复制代码)。但是,有几个   人们已经注意到经常使用   内嵌实际上创造得更慢   码。之所以是因为它可以   导致代码变大   超过适合一个或多个的大小   运行时使用的更多缓存。作为一个   结果导致部分原因   进出缓存的功能   每次通过一些循环和   缓存未命中和后续重新加载   比功能更昂贵   打电话给另一个人   缓存页面。这很有趣   情况和一个不可能的情况   预测,只能通过试验和观察   错误。

你的同事似乎愿意完全抛弃一种非常好的技术,因为在某些情况下它可能会破坏指令缓存的效率。在大多数系统上运行的大多数程序不需要对指令高速缓存进行太多的微观管理。

在通常的情况下,与内容相比,您更有可能获得内联的性能提升。此外,除非您专门禁用它,否则编译器将为您内联。

这里有一个教训,那就是你应该非常关注编译器对性能密集型代码的处理。但是对于其他80%的代码,编译器通常会自己做正确的事情。

(同样值得指出的是,内联代码总是比原始代码更大。我经常观察到这是重浮点或SIMD代码的情况。)< / p>