在复制构造函数中调用赋值运算符

Posted

技术标签:

【中文标题】在复制构造函数中调用赋值运算符【英文标题】:Calling assignment operator in copy constructor 【发布时间】:2011-02-08 00:03:15 【问题描述】:

复制构造函数的这种实现有一些缺点吗?

Foo::Foo(const Foo& i_foo)

   *this = i_foo;

我记得,在某本书中建议从赋值运算符调用复制构造函数并使用众所周知的交换技巧,但我不记得了,为什么...

【问题讨论】:

***.com/questions/1533725/…的可能重复 另一个重复:***.com/questions/1457842/… 相关:***.com/questions/1477145/…***.com/questions/1734628/…***.com/questions/2034635/… 那会是哪本书?调用通用代码(可能在命名的私有函数中)来执行复制是一种很好的做法。但是要从复制构造函数中使用赋值运算符呢?在大多数情况下 - 没有。 【参考方案1】:

是的,这是个坏主意。所有用户定义类型的成员变量都会先被初始化,然后立即被覆盖。

那个交换技巧是这样的:

Foo& operator=(Foo rhs) // note the copying

   rhs.swap(*this); //swap our internals with the copy of rhs
   return *this;
 // rhs, now containing our old internals, will be deleted 

【讨论】:

“那是个坏主意”,IMO 的表述过于宽泛。通过这样说,您可能会过早地进行微优化。如果您在 ctor 和 op=() 中都编写分配代码,则会创建代码重复并违反 DRY。弄清楚双重初始化是否涉及成本,将其与降低代码库复杂性所获得的收益进行比较,然后决定哪种方式是正确的。 swap 技巧的好处是它自动处理自赋值,并且(假设.swap() 不抛出)具有很强的异常安全性,因为赋值成功或失败保持不变并引发异常。 除了这些天,这个技巧通过值传递 rhs 做得更好。见cpp-next.com/archive/2009/08/want-speed-pass-by-value @Kaz:你是对的。 <sigh> 这几天我不得不承认我做某事主要是出于旧习惯,而不是为什么它(仍然)被认为是最先进的。也许我老了。 @JohnDibling 从 copy ctor 调用 op=() 并不是避免代码重复的唯一方法,而且我认为在所有技术中,它仍然不是一个很好的方法。【参考方案2】:

在构造函数中调用 operator=() 既有潜在的缺点,也有潜在的好处。

缺点:

无论您是否指定值,您的构造函数都会初始化所有成员变量,然后operator= 将再次初始化它们。这增加了执行的复杂性。您需要就何时会在您的代码中产生不可接受的行为做出明智的决定。

您的构造函数和operator= 变得紧密耦合。实例化对象时需要做的所有事情也将在复制对象时完成。同样,您必须聪明地确定这是否是一个问题。

收获:

代码库变得不那么复杂并且更易于维护。再一次,明智地评估这一收益。如果您有一个包含 2 个字符串成员的结构,则可能不值得。另一方面,如果您有一个包含 50 个数据成员的类(您可能不应该,但这是另一篇文章的故事)或彼此之间具有复杂关系的数据成员,那么只有一个可能会带来很多好处init 函数,而不是两个或更多。

【讨论】:

【参考方案3】:

您正在寻找Scott Meyers' Effective C++,第 12 项:“复制对象的所有部分”,其摘要指出:

复制函数应确保复制对象的所有数据成员及其所有基类部分。 不要试图实现其中一个复制功能。相反,将通用功能放在第三个功能中 打电话。

【讨论】:

以上是关于在复制构造函数中调用赋值运算符的主要内容,如果未能解决你的问题,请参考以下文章

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

是否为数组/向量插入调用了赋值运算符或复制构造函数?

C++:在派生类构造函数中调用基类赋值运算符的错误形式?

当我对具有复制构造函数但没有赋值运算符的对象进行赋值时会发生啥?

如何从基类调用派生赋值运算符?

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