【发布时间】:2020-06-25 16:40:51
【问题描述】:
在定义类时,现在通常使用= default 进行析构函数/复制构造函数和复制赋值。查看我的代码库,这些几乎总是只在头文件中,但有些同事已将它们放在 .cpp 文件中。在这种情况下,最佳做法是什么?
当这些函数位于标头中并依赖链接器对它们进行重复数据删除时,编译器是否会多次生成这些函数。如果您有一个庞大的班级,是否只值得将它们放在.cpp 文件中?对于我们大部分是旧的 C++98 代码,什么都不做的函数通常也只定义在头文件中。什么都不做虚拟析构函数似乎经常被移到.cpp 文件中。对于需要其地址来填充虚拟方法表的虚拟方法来说,它是否(或曾经)在某种程度上很重要。
还建议将noexcept() 子句放在= default 函数上吗?编译器似乎是自己派生的,因此它仅在存在时用作 API 文档。
【问题讨论】:
-
你的意思是把
MyClass() = default;放在头文件的类定义中还是把MyClass::MyClass() = default作为cpp中的构造函数定义? -
前几天这个咬到我了:如果你有一个
std::unique_ptr<forward_declared_class>成员,并且想隐藏forward_declared_class,你需要将析构函数放在.cpp 中。否则,包含标题的任何人也将需要您要隐藏的类的定义。 -
@molbdnilo 具有唯一指针的 PIMPL 就是一个众所周知的例子。 eerorika 的答案应该得到更多关注,因为其他两个投票较多的答案都强烈推荐仅使用头文件的解决方案。
-
@DanielLangr imo 定义显式默认的特殊成员函数的微妙之处,特别是对于默认构造函数,除了它们的第一个声明之外,再加上对“统一初始化语法”
S s{};故障安全的常见误解恕我直言,在众多代码库中为 UB 灾难提供了完美的配方,大多数开发人员不知道这些细节,并且“模式”可能会在不被理解的情况下迅速传播。对于 PIMPL 实现,即使显式默认构造函数非常适合 ... -
... 特定用例,可能值得考虑自己明确定义它,例如通过一个典型的语义上合理有序的成员初始化器列表。不过,有不同的答案很好,但对我来说,安全总是胜过聪明(不过,我确实在安全关键领域工作)。