令operator=返回一个*this的引用
在重载=,+=,*=等运算符时,令其返回一个指向this的引用。
class MyClass {
int* val;
public:
MyClass(int i) : val(new int(i)){}
MyClass():val(new int(0)){}
void print() {
cout << *val << endl;
}
MyClass& operator=(const MyClass& m) {
delete val;
val = new int(*m.val);
return *this;
}
};
这种情况可以实现连锁赋值。
MyClass a,b;
MyClass c(32);
a = b = c;
在重载赋值运算符时处理“自我赋值”
上面的代码,我们做如下的尝试。
在执行b=b时,因为两个对象指向同一块内存,在执行delete val语句时,会将内存释放掉。这样的做法无疑是有问题的。
正常的代码中,自我赋值出现的概率很小,但是隐性的自我赋值可能会出现。例如,
list[i] = list[j];
*px = *py;
void func(BaseClass* base, DerivedClass* derived){...}
其中i=j时,list[i] 与 list[j]相同;px与py相同时,指向同一块内存;而父类指针与子类指针甚至可以同时指向同一个子类对象。
如果在以上这种隐式的自我赋值的情况出现时,会给调试带来很大困难。我们用两种方式来避免这种情况:
1,在深度复制前检测是否相同
class MyClass {
int* val;
public:
MyClass(int i) : val(new int(i)){}
MyClass():val(new int(0)){}
void print() {
cout << *val << endl;
}
MyClass& operator=(const MyClass& m) {
if(this == &m)
return *this;
delete val;
val = new int(*m.val);
return *this;
}
};
如果两个地址相同,那么就什么都不做,直接返回*this。
2,用其他指针来销毁空间
MyClass& operator=(const MyClass& m) {
int* temp = m.val;
val = new int(*m.val);
delete temp;
return *this;
}
在销毁val之前,用临时指针temp来代替val,然后在val修改之后,通过temp来释放空间。这样就保证了自我赋值的正确性。
建议使用第二种方式。
因为,这样避免了异常带来的影响。如果在new 的过程中出现异常,第二种方式保证了val指向可用的数据。而第一种方法会先销毁val,而new的过程如果出现异常,val会指向不明确的内存。