初始化列表:复制构造函数和赋值运算符 = 冗余?

Posted

技术标签:

【中文标题】初始化列表:复制构造函数和赋值运算符 = 冗余?【英文标题】:Initializer lists: copy constructors and assignment operators = redundancy? 【发布时间】:2011-12-24 20:18:18 【问题描述】:

对于您的类构造函数,初始化列表似乎是good idea,我假设对于复制构造函数也是如此。对于赋值运算符,必​​须为函数主体中的每个成员赋值。考虑以下简单块:

class Foo 
private: 
  int a,b;
public:
  Foo(int c, int d)  : a(c), b(d) 
  Foo(const Foo & X) : a(X.a), b(X.b) 
  Foo& operator=(const Foo& X) 
    if (this == &X) return *this;
    a = X.a;
    b = X.b;
    return *this;
  
;

如果一个类的数据成员数量适中,则在三个地方可能会弄乱不同的分配/初始化。我的意思是,如果复制构造函数看起来像:

  Foo(const Foo & X) : a(X.a), b(X.a) 

或者operator=中缺少一行。由于赋值运算符和复制构造函数通常具有相同的效果(因为我们将成员从一个 Foo 复制到另一个)我可以“重用”来自复制构造函数或赋值运算符的代码,反之亦然?

【问题讨论】:

获得正确的复制构造函数和赋值运算符比看起来要困难得多;你真的应该阅读FAQ on the copy-and-swap idiom。另一方面,正如其他人所说,在这种情况下,不需要用户声明的复制构造函数和赋值运算符。 我通常在构造函数中使用operator=:Foo(const Foo & X) *this = X; @freerider:一般来说,如果你的班级管理operator=在复制之前需要释放的资源,这不是一个好主意。 @matteo italia:我知道,但我刚刚回答了这个示例中不包含堆分配的问题。当然你不应该在更复杂的类中使用。 【参考方案1】:

将所有内容转发给赋值运算符可能无效,但这在允许的 C++03 中很常见。

在 C++11 中构造函数更简单:将所有构造函数转发给一个主构造函数。

class Foo 
private: 
  int a,b;
public:
  Foo(int c, int d)  : a(c), b(d) 
  Foo(const Foo & X) : Foo(x.a, x.d)  
  //syntax may be wrong, I don't have a C++11 compiler
  Foo& operator=(const Foo& X) 
    if (this == &X) return *this;
    a = X.a;
    b = X.b;
    return *this;
  

在 C++03 中(允许的地方)

class Foo 
private: 
  int a,b;
  void init(int c, int d) a=c; b=d;
public:
  Foo(int c, int d)  : init(c,d);
  Foo(const Foo & X) : init(X.a, X.b); 
  Foo& operator=(const Foo& X)  init(X.a, X.b); 

但请记住,这个不能在一些常见的情况下使用。 (任何不可分配的对象)

【讨论】:

【参考方案2】:

您的目标应该是根本不编写复制构造函数/赋值运算符。你的目标应该是让编译器来做。标准库容器都是可复制的,因此请在合理的情况下使用它们。

如果存在无法正确复制的成员,则使用智能指针或其他 RAII 对象。这些对象应该需要特殊的复制构造函数/赋值。他们只需要他们一个成员。

其他一切都不应使用它们。

【讨论】:

构造函数很常见,可以做出很多选择。不过我同意分配。 @MooingDuck:但是 copy 构造函数可以留给编译器编写(至少如果让编译器编写复制赋值运算符是安全的,它应该是安全的) . ... = default; 是你的朋友;使用它。 @MooingDuck 编译器,甚至是 VC2010,都会尽可能为你创建一个拷贝构造函数和拷贝赋值运算符。唯一不会的情况是您的成员没有复制构造函数/赋值。 @MooingDuck:为什么?只需要让编译器生成一个做正确的事,即使你定义了其他构造函数,你也不必定义它。【参考方案3】:

由于赋值运算符和复制构造函数通常具有相同的效果。

一点也不,一个做初始化,另一个做赋值。它们在对象的初始状态上是不同的,它们的任务是分开的(尽管相似)。规范赋值运算符通常是这样完成的:

Foo& operator=(Foo right) 
    right.swap( *this );
    return *this;

【讨论】:

以上是关于初始化列表:复制构造函数和赋值运算符 = 冗余?的主要内容,如果未能解决你的问题,请参考以下文章

c++中拷贝构造函数和赋值运算符重载本质上一样么

依赖项没有复制 ctor 或赋值运算符时的 C++ 初始化程序列表

从复制构造函数调用默认赋值运算符是不好的形式吗?

复制构造函数的运用

构造函数初始化列表----主要为解决const变量的赋值而生

[QT入门篇]3 QObject的拷贝构造函数与赋值运算符