(避免使用)C ++中的复制机制

时间:2011-10-31 20:01:58

标签: c++

  

可能重复:
  Why copy constructor and assignment operator are disallowed?

我正在从一个坚实的C背景学习C ++,并且急于避免我从reddit和黑客新闻中收集到的以前C ++的错误,我一直在使用Google C++ style guide和LLVM的源代码作为我自己的代码的参考。突出的一点是项目使用以下代码。以下内容来自LLVM的include / Support / MemoryBuffer.h:

MemoryBuffer(const MemoryBuffer &); // DO NOT IMPLEMENT
MemoryBuffer &operator=(const MemoryBuffer &); // DO NOT IMPLEMENT

Google echoes这种用法。显然,禁用这些“复制构造函数”是一件好事。

所以我的问题是:为什么这些东西如此可怕,(如果它们没有防范)它们的用途是什么样的,它在代码中会产生什么影响?

4 个答案:

答案 0 :(得分:9)

当对象必须管理自己的资源(例如内存或系统句柄)时,默认的复制构造函数和赋值运算符不再适用。这意味着你必须覆盖它们。

但是,有时复制这样的对象没有任何意义。或者,换句话说,某些对象不应被复制。有时甚至不可能编写这样的构造函数或赋值运算符。在这种情况下,最好禁用复制和分配,以确保它们不会被意外复制。

标准iostream就是一个很好的例子。

总而言之,这是一个特殊情况。绝对不是你经常会遇到的事情。

答案 1 :(得分:6)

对于复制构造函数,没有可怕,并且它们不应被视为这样。

然而,也就是说,有时复制对象根本没有意义。在您提供的示例中,内存缓冲区是一个很好的复制示例。它可能已用于存储已分配对象的数据。副本提供什么?所有这些对象的原始数据的副本,而不是其他的(例如,对象不能使用它来取消分配)。

因此,一旦我们确定复制我们的类是没有意义的,那么我们应该阻止程序员这样做也是有意义的。如果我们不自己声明它们,编译器将偷偷摸摸并为我们制作默认的复制构造函数和赋值运算符。所以,如果我们声明它们(我们不需要提供实现)并确保这些声明是私有,那么如果程序员试图这样做,编译器将发出编译错误

答案 2 :(得分:3)

在C ++ 03中,声明一个没有定义为私有的复制构造函数和赋值运算符是一种阻止人们复制类实例的方法。如果有人试图这样做,他们会得到一个编译错误,抱怨赋值运算符和复制构造函数是私有的。另外,如果类自己的方法尝试使用这些函数,则不提供定义,它们将获得链接器错误,抱怨操作符和复制构造函数未定义。这是必要的,因为否则编译器将为您生成一个复制构造函数和赋值运算符,这通常不会执行您想要的操作(浅拷贝)。

在新的C ++ 11标准中,有一种更好的方法可以做到这一点。 delete关键字可以在声明中使用,如:

struct NoCopy
{
    NoCopy & operator =( const NoCopy & ) = delete;
    NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //compilation error, copy ctor is deleted

取自here

的示例

答案 3 :(得分:3)

有时,制作副本没有意义(例如,std::unique_ptr,互斥对象或数据库连接对象等。或者制作副本效率低下,并且您希望阻止此操作。或者正确实现复制构造函数是痛苦和脆弱的,并且您不希望依赖它的存在。删除复制语义是通过将复制构造函数声明为私有来完成的。

另一种流行的选择是继承boost::non_copyable

C ++ 11标准提供了无处不在使用复制构造函数的替代方法。这是以移动语义的名称,并允许您移动对象而不是复制它们。移动语义几乎总是有意义的:从函数返回一个对象会移动它。您还可以通过值显式地将对象移动到某个函数中。

请注意,C ++ 11原则上允许您以这种方式删除复制语义:

struct foo
{
    foo(const foo&) = delete;
    void operator=(const foo&) = delete;
};

而不是将它们设为私有,就像在C ++ 03中一样。

每当我必须声明并实现一个复制构造函数时,我现在发现自己几乎总是禁用复制语义。有一些习惯,这是有道理的:如果复制语义不是微不足道的,那么复制有可能出现问题。一个值得注意的例外是引用计数(参见类模板std::shared_ptr)。