不允许转让和转移价值

时间:2012-01-29 20:06:32

标签: c++ copy-constructor assignment-operator

根据我的理解,我可以通过定义私有拷贝构造函数和赋值运算符来“禁用”复制和分配给我的对象:

class MyClass
{
private:
    MyClass(const MyClass& srcMyClass);
    MyClass& operator=(const MyClass& srcMyClass);
}

但这是什么用途?
这被认为是一种不好的做法吗?

如果您能描述一下这种情况,我将不胜感激,以这种方式“禁用”赋值和复制构造函数是合理/有用的。

5 个答案:

答案 0 :(得分:11)

当你的对象被复制没有意义时,这很有用。这绝对不是不好的做法。

例如,如果您有一个表示网络连接的类,则复制该对象没有意义。另一次你可能希望一个类是不可复制的,如果你有一个类代表多人游戏中的一个玩家。这两个类都代表了在现实世界中无法复制的东西,或者复制(一个人,一个连接)没有意义的东西。

此外,如果您尝试实现Singleton,则使对象不可复制是标准过程。

答案 1 :(得分:5)

一般来说,管理资源的任何类都应该是不可复制的或具有专门的复制语义。反之亦然:任何不可复制或需要专门复制语义的类都在管理资源。在实践中,“管理资源”在C ++语言中意味着负责内存中的某些空间,或者与网络或数据库的连接,或文件的句柄,或撤销事务等等。

资源管理捕获了大量示例。这些是承担前缀操作,后缀操作以及可能之间的某些操作的职责。例如,内存管理涉及获取我们将管理的内存地址的句柄,可能会弄乱内存,最后释放句柄(因为如果你喜欢某些东西,那就让它免费)。

template<typename T>
struct memory {
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p }
    T& operator*() const { return *p; }
private:
    T* p;
};

// ...
{
    memory<int> m0;
    *m0 = 3;
    std::cout << *m0 << '\n';
}

这个memory类几乎是正确的:它自动获取底层内存空间并自动释放它,即使异常在获取其资源后传播一段时间也是如此。但请考虑这种情况:

{
    memory<double> m1(3.14);
    memory<double> m2(m1);  // m2.p == m1.p (do you hear the bomb ticking?)
}

因为我们没有为memory提供专门的复制语义,所以编译器提供了自己的复制构造函数和复制赋值。这些是错误的事情:m2 = m1表示m2.p = m1.p,这样两个指针指向同一个地址。这是错误的,因为当m2超出范围时,它会像一个好的负责对象一样释放它的资源,当m1超出范围时,它也释放了它的资源,同样的资源m2具有已经释放,完成双删除 - 臭名昭着的未定义行为场景。而且,在C ++中,很容易制作一个对象的副本,甚至没有注意到:一个函数按值获取参数,按值返回参数,或者通过引用获取参数,然后调用另一个函数,它自己接受(或返回)它的参数参数值...更容易假设试图复制。

所有这一切都要说,当一个班级'存款管理资源时,你应该立即知道你需要处理复制。你应该决定

  • 你支持复制,而你决定复制意味着什么:安全共享资源,执行底层资源的深层副本,以便不进行任何共享,或者将copy-on-write或懒惰副本中的两种方法结合起来。无论您选择哪条路径,您都需要提供专门的复制构造函数复制赋值运算符。
  • 或者您不支持任何类型的资源复制,在这种情况下,您将禁用复制构造函数和复制赋值运算符。

我走得太远,并说资源管理是禁用复制或提供专门的复制语义的唯一情况。这只是The Rule of Three的另一个视角。

答案 2 :(得分:2)

这是一种非常普遍的做法。有很多例子都不适合复制。

假设您的对象代表一个开放的服务器端套接字(即传入的网络连接);制作该对象副本的语义是什么?

答案 3 :(得分:0)

当您尝试实现单例模式时,使用私有构造函数是完全可以接受的,因为它只是在自身内部和其他地方实例化。 调用后,无法撤消构造函数。因此,只有在检查单例条件是否满足后才会调用构造函数。

答案 4 :(得分:0)

当你被允许创建对象时,只有在单例的情况下检查才需要私有构造函数。当调用构造函数时,将调用对象实例,然后检查是否已有另一个实例没有意义。所以我们所做的是从main和内部成员函数调用类的成员函数检查另一个实例是否已经在内存中。如果不是构造函数被调用。别的中止了。 检查单例类或其他受保护的类,其中对象的数据必须保证安全,不应该被允许复制。

另请查看:Singleton Class in C++