复制构造函数和复制赋值运算符 (C++)

注意

从 C++11 中开始,该语言支持两类赋值:复制赋值移动赋值。 在本文中,“赋值”意味着复制赋值,除非有其他显式声明。 有关移动赋值的信息,请参阅移动构造函数和移动赋值运算符 (C++)

赋值操作和初始化操作都会导致对象被复制。

  • 赋值:将一个对象的值分配给另一个对象时,第一个对象将复制到第二个对象。 因此,此代码将 b 的值复制到 a

    Point a, b;
    ...
    a = b;
    
  • 初始化:在声明新对象、按值传递函数参数或从函数返回值时,将发生初始化。

您可以为类类型的对象定义“复制”的语义。 例如,假设有以下代码:

TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;

前面的代码可能表示“将 FILE1.DAT 的内容复制到 FILE2.DAT”,也可能表示“忽略 FILE2.DAT 并使 b 成为 FILE1.DAT 的另一个句柄”。必须将合适的复制语义附加到每个类,如下所示:

  • 使用返回对类类型的引用的赋值运算符 operator=,并采用 const 引用传递的一个参数,例如 ClassName& operator=(const ClassName& x);

  • 使用复制构造函数。

如果不声明复制构造函数,编译器将为你生成成员的复制构造函数。 同样,如果不声明复制赋值运算符,编译器将为你生成成员的复制赋值运算符。 声明复制构造函数不会取消编译器生成的复制赋值运算符,反之亦然。 如果实现其中任一方法,我们建议也实现另一个。 实现这两者时,代码的含义是明确的。

复制构造函数采用 ClassName& 类型的参数,其中 ClassName 是类的名称。 例如:

// spec1_copying_class_objects.cpp
class Window
{
public:
    Window( const Window& );            // Declare copy constructor.
    Window& operator=(const Window& x); // Declare copy assignment.
    // ...
};

int main()
{
}

注意

尽可能创建复制构造函数的参数 const ClassName& 的类型。 这可防止复制构造函数意外更改复制的对象。 它还允许从 const 对象复制。

编译器生成的构造函数

编译器生成的复制构造函数(如用户定义的复制构造函数)具有单个自变量类型“对 class-name 的引用”。当所有基类和成员类都具有声明为采用 constclass-name& 类型的单个自变量的复制构造函数时,将引发异常。 在这种情况下,编译器生成的复制构造函数的自变量也是 const

当复制构造函数的自变量类型不是 const 时,复制 const 对象进行初始化将产生错误。 反之则不然:如果自变量是 const,可以复制不是 const 的对象进行初始化。

编译器生成的赋值运算符遵循 const 的相同模式。 除非所有基类和成员类中的赋值运算符都采用 const ClassName& 类型的自变量,否则它们将采用 ClassName& 类型的单个自变量。 在这种情况下,类的生成的赋值运算符采用 const 自变量。

注意

当虚拟基类由复制构造函数(编译器生成或用户定义的)初始化时,将只初始化一次:在构造它们时。

含义类似于复制构造函数的含义。 当自变量类型不是 const 时,从 const 对象赋值将产生错误。 反之则不然:如果将 const 值赋给不是 const 的值,则赋值能成功。

有关重载赋值运算符的详细信息,请参阅赋值