3

我最近读到,三法则,我想知道我是否违反了它?

在我的 GUI 应用程序中,像MainFrame, Interface,CircuitBreadboard类(类名是指示性的)每个都有一个实例。在它们的构造函数中,我分配了一些资源(内存),我在它们的析构函数中安全地释放了这些资源。

所以我只定义了析构函数,没有定义复制构造函数赋值运算符

我确定我不需要它们,但我很好奇我是否违反了规则,我可以/应该做什么来遵守它?

4

5 回答 5

6

三规则是关于处理所有三巨头,但这并不一定意味着如果你不想定义它们,你就必须定义它们。您要么提供它们,要么禁止它们。你不应该做的是忽略它们。


所以我只定义了析构函数,但没有定义复制构造函数和复制运算符。
我违反了三原则吗?

是的,你违反了规则。编译器会生成一个拷贝构造函数和拷贝赋值运算符,并且由于你在构造函数中分配内存并在析构函数中释放,这些拷贝会有错误的语义:它们会拷贝指针,并且你会有两个类对同一个内存进行别名. 该分配甚至不会释放旧内存,而只是覆盖指针。

这是一个问题吗?

如果像您暗示的那样,您不复制或分配给这些类的实例,则不会出错。但是,为了安全起见,最好将复制构造函数和复制赋值运算符声明(甚至不要定义)私有,这样您就不会意外调用它们。

在 C++11 中,您可以使用以下= delete语法:

T(T const&) = delete; // no copy constructor
T& operator=(T const&) = delete; // no copy assignment
于 2012-02-04T07:13:01.940 回答
2

您应该声明(但不实现)私有复制构造函数和赋值运算符。确保您没有实现这些功能。这将防止对不应复制的类进行任何类型的复制。

于 2012-02-04T07:13:06.060 回答
2

这在很大程度上取决于您的应用程序逻辑以及您如何向用户记录接口类。

通常,一个优秀的 c++ 程序员必须了解规则三(如果您知道“复制和交换习惯用法”,则为一半)和 5 以及 c++11(移动语义)的 1/2。

如果你的类管理资源并且如果同一个类是可复制的(即复制ctor和赋值运算符未定义为私有),那么通过编写你自己的复制ctor和赋值运算符来进行深度复制非常重要。

但是,如果您总是通过将它们作为 REFERENCE 传递来玩弄您的类,那么最好将默认的复制 ctor 和赋值运算符定义为私有,这样即使您通过 valy 或错误地复制,编译器也会警告您。

于 2012-02-04T07:20:06.690 回答
1

是的,根据该定义,它确实违反了三规则。

然而,这是一个“经验法则”。一般准则。如果您不需要复制构造或赋值操作,请不要实现它们。其他人建议将它们声明为私有并将它们定义为空。我会更进一步,说甚至不要定义它们。

如果您定义它们,那么您可能仍然会调用空方法。相反,不要定义它们,如果您尝试调用这些方法,您将收到链接器错误,因为找不到方法定义。优先考虑构建时错误而不是运行时错误/不良行为。

于 2012-02-04T07:17:47.257 回答
1

如果您不需要它,请不要遵循它。三法则背后的动机是,当您需要析构函数时,通常是因为您需要进行一些动态释放。

如果您也进行了释放,您也将需要复制构造函数和赋值运算符。想象一下,你有一个类,它有一个指向某物的指针:

struct Foo
{
    Foo() { ptr_ = new int; }
    ~Foo() { delete ptr_; }
    int* ptr_;
};

因为您没有定义复制构造函数和赋值运算符,所以无论何时复制 a Foo,原始和副本都将使用指向同一个int;的指针。当原始或副本被破坏时,指针被释放,而另一个则留下不可用的数据。

Foo(cont Foo& other) {
    other.ptr_ = new int(*ptr_);
}

// Same for operator=

如果您没有在构造函数/析构函数中进行任何动态分配,则很有可能您实际上不需要复制构造函数或赋值运算符(但不一定)。

于 2012-02-04T07:20:20.947 回答