? ? ? ? 在重载=,+=,*=等运算符时,令其返回一个指向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会指向不明确的内存。