提问人:Eric Z 提问时间:10/6/2011 更新时间:10/6/2011 访问量:829
为什么在三分法则中不考虑非默认构造函数?
Why is a non-default constructor NOT considered in the Rule of Three?
问:
三法则(也称为三巨头定律或三巨头定律)是C++中的经验法则,它声称如果一个类定义了以下其中之一,它可能应该显式定义所有三个:析构函数,复制构造函数,复制赋值运算符。
为什么非默认构造函数不被视为其中之一?当类中管理任何资源时,程序员无论如何都必须定义一个非默认构造函数。
答:
这不是三分法则的意义所在。
任何管理资源的类都将具有该析构函数,因此无论如何都适用三规则。关键是你不是绝对需要非默认构造函数,但你确实需要其他构造函数(复制构造函数/赋值运算符)。
至少它曾经对标准容器中的元素至关重要。
现在有了移动语义(c++11),事情开始发生了一些变化。会有 5 条规则吗?我不知道它在“最佳实践”和“经验法则”方面会如何发展。
事实上,人们已经可以陈述三法则的变体:定义析构函数的类还应该定义复制/移动构造函数和复制/移动赋值运算符。如果定义了复制构造函数,则还应定义复制赋值运算符。如果定义了移动构造函数,则还应定义移动赋值运算符。
无论如何,这将是我一段时间的经验法则。
为什么非默认构造函数不被视为其中之一?当类中管理任何资源时,程序员无论如何都必须定义一个非默认构造函数。
这不一定是真的。构造函数可能不会获取任何资源。其他功能也可能获得它们。事实上,可能有许多函数(包括构造函数本身)可能会获取资源。例如,在 的情况下,它是 和 哪个获取资源。因此,将构造函数视为可能获取资源的其他函数。std::vector<T>
resize()
reserve()
此规则的思路是,当您进行复制时,编译器生成的默认复制代码将不起作用。因此,您需要自己编写复制语义。由于类管理资源(哪个函数获取它并不重要),因此析构函数必须释放它,因为对于完全构造的对象,析构函数可以保证被执行。因此,您还必须定义析构函数。在 C++ 11 中,您还必须实现移动语义。move-semantic 的逻辑参数与 copy-semantic 的逻辑参数相同,只是在 move 语义中,您也更改了源。Move-semantic 很像器官捐献者;当你把你的器官交给别人时,你就不再拥有它了。
如果你获得任何资源(例如新的资源),你是完全正确的,但假设你不会忘记释放析构函数中的资源。一旦你创建了析构函数,三规则就开始了,你应该定义所有三个。
但是,例如,如果类中只有一些使用复制语义初始化的成员变量,则不需要析构函数,并且规则不适用。
定义一个非默认构造函数并不意味着你需要一个析构函数等 - 你可能只是为了方便填写 POD 类型而使用该构造函数。
也就是说,这同样适用于默认构造函数。
基本上,“你需要三巨头吗?”是在类管理资源时触发的。您可能仍然不需要默认构造函数,但确实需要构造函数来设置有效的初始状态。
该新构造的状态可能还没有资源的实例,但如果它没有,那么它必须知道它没有(例如,有一个空指针)。
然而,在很多情况下,三巨头也被隐含使用。例如,临时数据是默认构造的。您需要非默认构造函数的一个原因只是为了阻止提供和使用隐式默认构造函数。
因此,同时定义这三个代码的一个原因是确保您的代码(包括编译器隐式提供的代码)保持理智。
几乎总是,如果你正在管理一个资源,你会有一个默认的构造函数,所以这就是规则提到它的原因 - 但只要你定义某种构造函数,你应该没问题。
评论