2010-09-06 61 views
18

如果operator=被正确定义,那么可以使用以下作为复制构造函数吗?根据运算符实现复制构造函数=

MyClass::MyClass(MyClass const &_copy) 
{ 
    *this = _copy; 
} 
+9

使用[复制和交换成语(http://stackoverflow.com/questions/3279543/what-is-the-copy-and-交换-成语)。 – GManNickG 2010-09-06 15:28:03

+0

通常,复制赋值运算符将执行一些清理。如果你的类有一个指向动态分配内存的指针,复制赋值操作符应该做的第一件事是释放内存。这个拷贝构造函数的实现会给拷贝赋值操作符一个悬挂指针,而你不想删除它。 – Kevin 2014-11-08 22:40:14

+0

即使你使用智能指针(在这种情况下删除不会有问题),你仍然会毫无意义地默认构造并分配所有的成员变量。只需使用复制和交换。 – Kevin 2014-11-08 22:43:56

回答

27

如果MyClass的所有成员都有默认构造函数,那么是。

注意,通常是周围的其他方法:

class MyClass 
{ 
public: 
    MyClass(MyClass const&);  // Implemented 
    void swap(MyClass&) throw(); // Implemented 
    MyClass& operator=(MyClass rhs) { rhs.swap(*this); return *this; } 
}; 

我们operator=路过值,以使拷贝构造函数被调用。请注意,一切都是异常安全的,因为swap保证不会抛出(你必须在你的实现中确保这一点)。

编辑,根据要求,有关呼叫按值的东西:本operator=可以写成

MyClass& MyClass::operator=(MyClass const& rhs) 
{ 
    MyClass tmp(rhs); 
    tmp.swap(*this); 
    return *this; 
} 

C++的学生通常叫按引用传递类的实例,因为拷贝构造函数得到,如果他们被称为按价值传递。在我们的情况下,无论如何我们必须复制rhs,所以按价值传递很好。

因此,operator=(第一版,由值调用)读取:

  • 使rhs拷贝(通过拷贝构造函数,自动调用)
  • 交换与*this
  • 返回内容*this并让rhs(包含旧值)在方法出口处销毁。

现在,我们对该按价值拨打了额外奖金。如果传递给operator=的对象(或任何按值获取其参数的函数)是临时对象,则编译器可以(并且通常)根本不做任何复制。这叫做copy elision

因此,如果rhs是临时的,则不会进行复制。我们只剩下:

  • 交换thisrhs内容
  • 销毁rhs

所以路过的值是在这种情况下,不是通过引用传递高效。

+1

实际上,如果MyClass具有默认构造函数,则无关紧要。只有数据成员和基类有一个... – 2010-09-06 14:13:26

+0

是的,你说得对。 – 2010-09-06 14:15:59

+0

好的,谢谢。我这样做是为了避免代码重复执行'operator ='和拷贝构造函数。使用copy-and-swap成语,代码在复制构造函数和'swap'方法中被复制。我对吗? – gregseth 2010-09-08 07:42:41

5

该实现意味着所有数据成员(和基类)的默认构造函数都是可用的,并且可以从MyClass访问,因为它们将在进行赋值之前先被调用。即使在这种情况下,对构造函数进行额外的调用可能会很昂贵(取决于类的内容)。

我仍然坚持通过初始化列表单独实施复制构造函数,即使这意味着需要编写更多的代码。

另一件事:此实现可能有副作用(例如,如果您有动态分配的成员)。

0

如果MyClass分配内存或可变,我会说这是不好的。

+3

如果它不可变,那么它将不会有'operator =' - 这是一个变异函数。或者,我的意思不是像你一样可变吗? – 2010-09-06 18:11:29

11

根据异常安全拷贝构造函数实现operator =更合适。参见Herb Sutter的实例4,了解该技术的解释以及为什么这是一个好主意。

http://www.gotw.ca/gotw/059.htm

1

虽然最终结果是一样的,成员是第一默认初始化,只有后复制。

对于'昂贵'的成员,您最好使用初始化程序列表进行复制构建。

struct C { 
    ExpensiveType member; 

    C(const C& other): member(other.member) {} 
}; 



}; 
+0

您无法初始化外部施工。 – GManNickG 2010-09-06 15:27:34

+0

@GMan:darn。我的意思是写复制构造函数,而不是赋值。对不起。 – xtofl 2010-09-06 18:30:31

+0

没问题,删除-1。 :) – GManNickG 2010-09-06 19:09:03

0

是的。

个人而言,如果你的类没有指针,虽然我不会重载平等运算符或写入拷贝构造函数并让编译器为你做;它会实现一个浅拷贝,你会确定所有成员数据都被复制,而如果你重载= op;然后添加一个数据成员,然后忘记更新超载,您将遇到问题。

0

@Alexandre - 我不确定在赋值运算符中传递值。通过在那里调用拷贝构造函数你会得到什么好处?这是要扣紧赋值运算符吗?

P.S.我不知道如何写评论。或者可能是我不允许写评论。

+0

好的,我编辑我的答案提供更多的细节。 – 2010-09-06 18:12:44

+0

通常的参考文献是http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/。我仍然不相信结论总是正确的。 – 2010-09-06 18:17:22

+0

@Steve:对于'operator =',因为无论如何您都必须复制它,所以它不会更糟。 – 2010-09-06 18:24:03

0

技术上可以,如果你有一个工作赋值操作符(复制操作符)。

但是,你应该更喜欢复制和交换,因为:

  • 异常安全性是更容易复制交换
  • 关注最合乎逻辑的分离:
    • 的拷贝构造函数是约分配它需要的资源(复制其他东西)。
    • 的交换功能(主要是)只有关于交换内部的“手柄”,并不需要做的资源(DE)分配
    • 析构函数是关于资源释放
    • 复制和交换自然融合这三个功能委派/复印操作