我想将我的复制运算符重定向到我的复制构造函数。在后者中,我实现了基于旧可用类复制/构造新类的正确逻辑。
但是,正确的方法是如何做到这一点?我“认为”这可能是内存泄漏,但如果不传递指针,我不知道该怎么做:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
这个可以吗?如果不是,那么正确的方法是什么?我应该更改方法的主体还是原型?
谢谢你。
我想将我的复制运算符重定向到我的复制构造函数。在后者中,我实现了基于旧可用类复制/构造新类的正确逻辑。
但是,正确的方法是如何做到这一点?我“认为”这可能是内存泄漏,但如果不传递指针,我不知道该怎么做:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
这个可以吗?如果不是,那么正确的方法是什么?我应该更改方法的主体还是原型?
谢谢你。
不,operator=应该将当前对象属性设置为与分配的对象相同。您的方法在堆上分配一个新对象,将其作为引用返回(本质上是泄漏它),并使调用运算符的对象完全不变。
例如,您应该实现一个名为 的方法,CopyFrom()该方法分配所有对象的属性以匹配传入对象的属性(深度复制任何生命周期由 管理的堆分配指针MyClass),然后从您的复制构造函数和operator=.
class MyClass
{
public:
MyClass( const MyClass& in )
{
CopyFrom( in );
}
MyClass& operator=( const MyClass& in )
{
CopyFrom( in );
return *this;
}
private:
void CopyFrom( const MyClass& in )
{
... copies in's attributes into self.
}
};
除非您在 MyClass 中存储指针,否则正确的复制赋值运算符是默认生成的。但是,如果您需要实现一个,则可以通过 copy-swap 习惯用法按照您的复制构造函数编写它:
MyClass& MyClass::operator = (MyClass const& a) {
MyClass temp(a); // Call the copy constructor
using std::swap;
swap(temp, *this);
return *this;
}
的原因using std::swap是启用参数相关的查找。如果您为 MyClass 定义自己的交换函数,它将被调用。否则std::swap将用作后备。(编辑:在这种情况下,您实际上确实需要实现自定义交换,否则您将获得无限递归。std::swap将使用赋值运算符,它将调用std::swap,这将调用...)
这个成语之所以受欢迎,是因为std::swap它是一个无抛出函数。如果您的复制构造函数抛出异常,那么您分配给的对象仍处于有效状态。
通常,复制赋值运算符永远不应该创建副本。相反,它应该将数据复制到调用它的现有对象中(赋值的左侧)。例如:
class MyClass
{
public:
MyClass & operator = (const MyClass & RHS)
{
// Copy data from RHS into 'this'
m_value = RHS.m_value;
return *this;
}
private:
int m_value;
};
在这种情况下,不需要定义自己的复制构造函数,因为默认的(编译器提供的)可以正常工作。虽然这只是一个插图。
不幸的是,您不能在现有对象上调用复制构造函数。复制交换模式是一种替代方案,但效率可能较低。
您的代码完全错误(对不起)!赋值运算符不分配任何东西,而是分配一个指向 MyClass 对象的指针,从而造成内存泄漏。我的建议:避免使用指针或使用一些智能指针(shared_ptr、unique_ptr),但这只是一个旁注。
也许这会有所帮助:
#include <iostream>
#include <limits>
class X
{
public:
X(std::size_t n)
: m_size(n), m_data(new int[n])
{
std::cout << "Construct" << std::endl;
}
~X()
{
std::cout << "Destruct" << std::endl;
delete [] m_data;
}
// Exception safe assignment.
// Note: I am passing by value to enable copy elision and
// move semantics.
X& operator = (X x) {
std::cout << "Assign" << std::endl;
x.swap(*this);
return *this;
}
void swap(X& x) {
std::swap(m_size, x.m_size);
std::swap(m_data, x.m_data);
}
std::size_t size() const { return m_size; }
private:
std::size_t m_size;
int* m_data;
};
int main()
{
X x(1);
try {
x = X(2);
// To provoke an exception:
std::size_t n = std::numeric_limits<std::size_t>::max();
x = X(n);
}
catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << "Size: " << x.size() << std::endl;
return 0;
}
“正确的方法”是像赋值运算符一样实现赋值运算符:修改正在调用运算符的对象的内容并返回对它的引用。
您当前的实现将导致内存泄漏,并且不进行任何赋值(这是赋值运算符的要点)。
如果你只想写一次赋值代码,并且你的类没有在构造函数中分配内存,你可以这样做:
MyClass::MyClass(const MyClass& a) {
*this = a;
}
MyClass& MyClass::operator=(const MyClass& a) {
if (&a == this)
return *this;
// Do assignment
return *this;
}
但我不会推荐它。
如果您绝对想通过复制构造函数实现赋值运算符,请使用以下内容:
MyClass& MyClass::operator=(const MyClass& o)
{
this->~MyClass(); // destroy current object
new(this) MyClass(o); // use the copy constructor
return *this;
}
我想不出这将是最好的任何情况(其他答案描述了在某些情况下更好的实施方式)。
MyClass如果包含数百个 int/float 字段和几个动态分配的指针,也许(只是想在这里弥补) ?
NULLfirst但是,不鼓励在您的类中使用裸(非智能)指针。如果你有这样一个类,那么你遇到的问题比不工作的赋值运算符要糟糕得多——你必须先重构,问题就会连同所有其他错误一起消失。