Using copy constructor in assignment operator
在赋值运算符中使用复制构造函数是否违反样式指南?即。:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const Obj & Obj::operator=(const Obj & source) { if (this == &source) { return *this; } // deep copy using copy-constructor Obj * copy = new Obj(source); // deallocate memory this->~Obj(); // modify object *this = *copy; return *copy; } |
假设复制构造函数对对象执行深度复制。
编辑:
正如评论家指出的那样,我的代码是极其错误的。
至于总体概念性问题:正如whozcraig所建议的,复制/交换习语似乎是解决问题的方法:什么是复制和交换习语?
下面简单介绍一下示例中的复制/交换习惯用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include class Obj { int *p; void swap(Obj& left, Obj& right); public: Obj(int x = 0) : p(new int(x)) {} Obj(const Obj& s); Obj& operator = (const Obj& s); ~Obj() { delete p; } }; Obj::Obj(const Obj& source) : p(new int(*source.p)) {} void Obj::swap(Obj& left, Obj& right) { std::swap(left.p, right.p); } Obj & Obj::operator=(const Obj & source) { Obj temp(source); swap(*this, temp); return *this; } int main() { Obj o1(5); Obj o2(o1); Obj o3(10); o1 = o3; } |
为了了解它是如何工作的,我特意创建了一个成员,它是指向动态分配内存的指针(如果没有用户定义的复制构造函数和分配运算符,这将是有问题的)。
如果您关注赋值操作符,它会调用
当调用
另外,请注意,在赋值期间,如果
现在,前面给出了一个使用常用的"共享代码"方法复制分配的答案。以下是此方法的完整示例,并解释了其存在问题的原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Obj { int *p; void CopyMe(const Obj& source); public: Obj(int x = 0) : p(new int(x)) {} Obj(const Obj& s); Obj& operator = (const Obj& s); ~Obj() { delete p; } }; void Obj::CopyMe(const Obj& source) { delete p; p = new int(*source.p); } Obj::Obj(const Obj& source) : p(0) { CopyMe(source); } Obj & Obj::operator=(const Obj & source) { if ( this != &source ) CopyMe(source); return *this; } |
所以你会说"这是怎么回事?"好吧,问题是
问题在于,后续调用
是的,您可以通过创建一个临时指针,分配来轻松地修复它,最后,将临时指针分配给
举例来说,这里是
1 2 3 4 5 6 | void Obj::CopyMe(const Obj& source) { int *pTemp = new int(*source.p); delete p; p = pTemp; } |
但同样,您将看到大量使用"共享代码"方法的代码,这些方法有这个潜在的bug,并且没有提到异常问题。
你想做的事行不通。您似乎认为赋值运算符返回的对象变成了新的"this",但事实并非如此。您没有修改对象,只是将其销毁。
1 2 3 4 5 6 | Obj a; Obj b; a = b; // a has been destroyed // and you've leaked a new Obj. // At the end of the scope, it will try to destroy `a` again, which // is undefined behavior. |
使用placement
1 2 3 4 5 6 7 8 9 10 11 12 13 | const Obj & Obj::operator=(const Obj & source) { if (this == &source) { return *this; } this->~Obj(); new (this) Obj(source); return *this; } |
如果构造过程中可能出现异常,这将导致问题,并且可能导致派生类出现问题。
在复制构造函数和assigmnet运算符之间共享代码是完全有意义的,因为它们通常执行相同的操作(将对象作为参数属性传递给它)。
个人而言,我经常通过巧妙地对赋值运算符进行编码,然后从复制构造函数调用它来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Obj::Obj(const Obj & source) { Obj::operator=( source ); } const Obj& Obj::operator=(const Obj& source) { if (this != &source) { // copy source attribtes to this. } return *this; } |
如果您正确地编写
无论如何,您在两个函数之间共享代码的想法是好的,但是您实现它的方式却不好。一定要工作。它有很多问题,不能按你的意思去做。它以递归方式调用运算符。此外,您不应该像以前那样明确地调用析构函数(