为什么在C++中内联构造函数和析构函数不是一个好主意?

18

我记得在一本C++书中(相当长时间之前)阅读到过,特别是对于派生类,使用内联构造函数和析构函数并不是一个好主意。 我明白内联会导致目标代码膨胀,但还有其他设计上的考虑因素吗?当然,大多数编译器可能会拒绝内联并继续创建函数体,但如果它们内联化了,我们可能要付出什么代价?


注意:本书为《Effective C++ 第三版》,第30条目。 - rents
5个回答

41
编译器可以自由地内联您未声明为内联的代码,也可以自由地不内联您已声明为内联的代码。我见过编译器做这两件事情。因此,inline关键字并不意味着大多数人认为的那样。它的含义是允许例外一个定义规则,这样你可以将函数等放在头文件中而不会出现链接错误。
所以,建议是垃圾,让编译器决定何时最好进行内联和何时不进行内联。只需在需要防止链接错误的地方加上inline即可。

同样适用于registervolatile;现代编译器对这些建议进行了自由处理。 - s1n
14
@s1n 实际上不是这样的。关于编译器如何使用 volatile 变量,存在一些限制条件,这意味着这种变量将保持 volatile。在这方面,volatile 不是一个建议性的修饰符。 - didierc
一个清晰、简洁且完全正确的答案。我无法相信在今天还有这么多人认为 inline 实际上会指示编译器将方法定义内联。它只是一个链接修饰符,仅此而已。 - Andy Brown
编译器可自由内联代码,但仅在指定适当的“全部内联优化”时才会这样做。在GCC和VS中分别为--inline-functions或/ob2。当您在头文件中定义构造函数时,inline关键字是隐含的。因此,无论优化设置如何,所有这些都应该考虑进行内联处理。但是在VS中,我的测试似乎表明这种隐式内联要比显式指定关键字弱。即使是forceinline,在许多情况下也无法内联处理。forceinline > inline > 隐式inline > 自动内联。 - George Davison

5

我不知道构造函数,但析构函数往往是虚函数。在大多数情况下,编译器不会将虚函数内联,因为在运行时不知道将调用哪个覆盖。


1
你的论点是关于虚函数,而不是构造函数和析构函数。无论如何,它都不具有说服力。只有当通过指针或引用调用时,实际函数才是未知的。当在实际对象上调用时,它是已知的并且可以内联。声明一个很少内联的函数为内联仍然是完全有意义的。 - n. m.
@n.m.:我不是在谈论声明一个函数为inline(OP似乎也不是)。我说的是编译器是否会选择内联一个函数。我不确定OP的书想表达什么意思,这只是我的猜测。析构函数很常见地虚函数,并且很常在多态上下文中调用。但并非总是如此。我不确定哪一部分是令人难以信服的! - Oliver Charlesworth

5

绝对没有任何理由避免使用内联构造函数和析构函数。这本书是错误的,完全错误。


我认为OP指的是《Effective C++》。这本书明确指出,inline并不总是意味着代码更短或更快。它建议将方法内联作为一种应该非常谨慎考虑的优化形式。从这个意义上说,这本书既不正确也不错误。 - Kurt Pattyn

5
除了@oli在他的答案中提到的原因外:
该书可能指导不要将构造函数和析构函数内联,因为即使看似微不足道或空的函数也可能包含许多由编译器隐式生成的代码,并且实际函数定义可能会变得非常大。这可能导致代码膨胀。
话虽如此,明智的做法是让编译器决定是否内联函数调用(甚至是构造函数和析构函数),现代大多数编译器都会通过内联适当地优化函数。

2

我相信这与C++对代码的处理无关,因为它只是一个提示。

如果你开始考虑软件工程方面的事情,一切都会改变。所有内联函数的更改都将强制重新编译所有依赖文件。

当你维护一个库并想要发送一个修复bug的版本时,情况会变得更糟。内联函数不能简单地被另一个版本替换,因为调用代码可能没有重新编译。因此,当需要更改内联函数时,你必须移动到新版本的接口,而非内联函数可以随意替换。

再加上构造函数很少能够被编译器实际内联,那么我可以想象为什么书会给出提到的建议。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接