1

三法则(也称为三巨头法则或三巨头法则)是 C++ 中的一条经验法则,它声称如果一个类定义了以下其中之一,它可能应该显式定义所有三者:析构函数、复制构造函数、复制赋值运算符。

为什么非默认构造函数不被视为其中之一?当类中管理任何资源时,程序员无论如何都必须定义一个非默认构造函数。

4

4 回答 4

9

为什么非默认构造函数不被视为其中之一?当类中管理任何资源时,程序员无论如何都必须定义一个非默认构造函数。

这不一定是真的。构造函数可能不会获取任何资源。其他功能也可能需要它们。事实上,可能有许多函数(包括构造函数本身)可能会获取资源。例如,在 的情况下std::vector<T>,它是resize()并且reserve()获取资源。因此,将构造函数与可能获取资源的其他函数一样考虑。

此规则的想法是,当您进行复制时,编译器生成的默认复制代码将不起作用。因此,您需要自己编写复制语义。并且由于类管理资源(哪个函数获取它并不重要),析构函数必须释放它,因为析构函数保证会被执行,对于一个完全构造的对象。因此,您还必须定义析构函数。在 C++11 中,您还必须实现移动语义。移动语义的逻辑参数与复制语义的逻辑参数相同,只是在移动语义中,您也更改了。移动语义很像器官捐赠者。当你把你的器官给别人时,你就不再拥有它了。

于 2011-10-06T09:06:05.737 回答
2

这不是三法则的意义所在。

任何管理资源的类都将具有该析构函数,因此无论如何都适用三规则。关键是您并不绝对需要非默认构造函数,但您确实需要其他构造函数(复制构造函数/赋值运算符)。

至少它曾经对标准容器中的元素至关重要。

现在有了移动语义(c++11),事情开始发生了一些变化。会有5条规则吗?我不知道它在“最佳实践”和“经验法则”方面会如何发展。

事实上,已经可以说明三法则的一种变体:定义析构函数的类还应该定义复制/移动构造函数和复制/移动赋值运算符。如果定义了复制构造函数,则还应定义复制赋值运算符。如果定义了移动构造函数,还应该定义移动赋值运算符。

无论如何,这将成为我一段时间内的经验法则。

于 2011-10-06T09:05:55.667 回答
0

如果您获取任何资源(例如使用新资源),您是完全正确的,但假设您不会忘记在析构函数中释放资源。一旦你创建了析构函数,三个规则就会生效,你应该定义所有三个。

但是,例如,如果您的类中只有一些使用复制语义初始化的成员变量,则不需要析构函数并且该规则不适用。

于 2011-10-06T09:08:34.763 回答
0

定义非默认构造函数并不意味着您需要析构函数等 - 您可能只是使用该构造函数来方便填充 POD 类型。

也就是说,这同样适用于默认构造函数。

基本上,“你需要三巨头吗?” 当类将管理资源时触发。您可能仍然不需要默认构造函数,但确实需要构造函数来设置有效的初始状态。

新构建的状态可能还没有拥有资源的实例,但如果它不拥有,那么它必须知道它不拥有(例如,有一个空指针)。

然而,在很多情况下,三巨头也被隐含使用。例如,临时文件是默认构造的。您需要非默认构造函数的一个原因只是阻止提供和使用隐式默认构造函数。

因此,一次定义所有三个的原因之一是确保您的代码(包括编译器隐式提供的代码)保持正常。

几乎总是,如果您正在管理资源,您将有一个默认构造函数,这就是规则提到这一点的原因 - 但只要您定义某种构造函数,您就应该没问题。

于 2011-10-06T09:11:14.080 回答