在重载“operator=”时,若类中涉及到了资源的动态分配,则一定要考虑自我赋值的情况,否则容易造成内存泄露。
下面是一个不安全的示例:
class A{
public:
A& operator=(const A& a)
{
delete data;
size = a.size;
data = new int[size];
for(int i=0; i<size; i++)
data[i] = a.size[i];
return *this;
}
private:
int *data;
int size;
};
当出现自我赋值的情况时,data被释放掉了,而在循环中任然会试图访问之,显然这是非法的。
改正方法是增加“证同测试”
A& operator=(const A& a)
{
if(this == &a) return *this;
delete data;
size = a.size;
data = new int[size];
for(int i=0; i<size; i++)
data[i] = a.size[i];
return *this;
}
但是该版本可能具有“异常安全性”,因为如果分配时内存不足,会使得data指向一块被删除的内存,这样的指针式有害的。但你无法删除它,有无法读取他,很无奈。
一种解决方案是:不管“自我赋值”问题,只关注“异常安全”问题。示例如下:
A& operator=(const A& a)
{
int *pOrigin = data;
size = a.size;
data = new int[size];
for(int i=0; i<size; i++)
data[i] = a.size[i];
delete pOrigin;
return *this;
}
这样,即使new操作发生异常,data仍然保持原状,指向原来的内存。
另外一种,即确保“异常安全”又确保“自我赋值安全”的一个替代方案是:copy and swap 技术。
A& operator=(const A& a)
{
A temp(a); //为a做了一个副本
swap(temp); //将*this与a的副本交换。
return *this;
}
还有一种巧妙的方法是,利用copy assignment的实参的值传递,因为值传递会构建一个副本
A& operator=(A a) //a是实参的一个副本
{
swap(temp); //将*this与a的副本交换。
return *this;
}