我最近读过Rule of three,我想知道我是否违反了它?
在我的GUI应用程序中,MainFrame
,Interface
,Circuit
,Breadboard
等类(类名称是指示性的)具有每个类的单个实例。在他们的构造函数中,我已经分配了一些资源(内存),我可以在它们的析构函数中安全地释放它们。
所以我只定义了析构函数,但没有定义复制构造函数和赋值运算符。
我确信我不需要它们,但如果我违反规则,我很好奇,我可以/应该做些什么来遵循它?
答案 0 :(得分:6)
三条规则是关于与所有三巨头交易,但这并不一定意味着如果你不愿意,你必须定义它们。要么你提供它们要么禁止它们。你不应该做的就是忽略它们。
所以我只定义了析构函数,但没有定义复制构造函数和复制操作符 我是否违反了三条规则?
是的,你违反了规则。编译器将生成一个复制构造函数和复制赋值运算符,并且由于您在构造函数中分配内存并在析构函数中释放,因此这些副本将具有错误的语义:它们将复制指针,并且您将有两个类别于同一内存。赋值甚至不会释放旧内存,只是覆盖指针。
这是一个问题吗?
如果,就像你暗示的那样,你没有复制或分配给那些类的实例,什么都不会出错。但是,最好是安全起见并声明(甚至不要定义)复制构造函数和复制赋值操作符私有,因此不要意外调用它们。
在C ++ 11中,您可以改为使用= delete
语法:
T(T const&) = delete; // no copy constructor
T& operator=(T const&) = delete; // no copy assignment
答案 1 :(得分:2)
您应声明(但不实现)私有拷贝构造函数和赋值运算符。确保您没有实现这些功能。这将阻止任何类型的复制不应复制的类。
答案 2 :(得分:2)
这很大程度上取决于您的应用程序逻辑以及您如何向用户记录您的接口类。
通常,一个好的c ++程序员必须知道三个规则(如果你知道“复制和交换习语”,则为半个),而对于c ++ 11(移动语义),则必须知道一个半和1/2。
如果你的类管理资源,并且同一个类是可复制的(即复制ctor和assigment操作符未定义为私有),则通过编写自己的复制ctor和赋值操作符进行深度复制非常重要。
但是如果你总是通过将它们作为REFERENCE传递给你的类,那么最好将默认的复制ctor和赋值运算符定义为private,这样即使你通过valy或者错误地复制,编译器也会警告你。
答案 3 :(得分:1)
是的,根据该定义,它确实违反了三条规则。
然而,这是一个“经验法则”。一般准则。如果您不需要复制构造或赋值操作,请不要实现它们。其他人建议将它们声明为私有并将其定义为空。我更进一步说,甚至不定义它们。如果你定义它们,那么你可能仍然会调用空方法。相反,保留它们未定义,如果您尝试调用这些方法,则会收到链接器错误,因为找不到方法定义。支持运行时错误/不良行为的构建时错误。
答案 4 :(得分:1)
如果您不需要,请不要遵循它。三条规则背后的动机是,当你需要一个析构函数时,通常是因为你需要做一些动态的解除分配。
如果你也进行解除分配,你也需要复制构造函数和赋值运算符。想象一下,你有一个指向某个东西的类:
struct Foo
{
Foo() { ptr_ = new int; }
~Foo() { delete ptr_; }
int* ptr_;
};
因为您没有定义复制构造函数和赋值运算符,所以每当您复制Foo
时,原始复制和复制都将使用指向同一int
的指针;当原始文件或副本被销毁时,指针被释放,而另一个则无法使用数据。
Foo(cont Foo& other) {
other.ptr_ = new int(*ptr_);
}
// Same for operator=
如果你没有在构造函数/析构函数中进行任何动态分配,那么你很可能实际上不需要复制构造函数或赋值运算符(但不一定)。