在 C++ 中,为什么要重写赋值运算符?
注意异常安全和自赋值问题:
//非异常安全版本
CMyString& CMyString::operator =(const CMyString &str)
{
if(this == &str)
return *this;
delete []m_pData;
m_pData = NULL;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
我们知道,在分配内存时有可能发生异常。当执行语句:
new char[strlen(str.m_pData) + 1]
发生异常时,程序将从该赋值运算符的重载函数退出不再执行。注意到这个时候语句 delete []m_pData 已经执行了。也就是说赋值操作没有完成,但原来对象的状态已经改变。
//异常安全版本
CMyString& CMyString::operator =(const CMyString &str) {
if(this != &str)
{
m_pData = new char[strlen(str.m_pData) + 1];
delete []m_pData;
m_pData = NULL;
return *this;
}
return *this;
}
异常安全版本, 基于拷贝交换技术
CMyString& CMyString::operator =(const CMyString str) //参数是非引用吧
{
if(this != &str)
{
char* pTemp = str.m_pData;
str.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}
该方案通过调用构造拷贝函数创建一个临时对象来分配内存。此时即使发生异常,对原来对象的状态没有影响。
交换临时对象和需要赋值的对象的字符串指针之后,由于临时对象的生命周期结束,自动调用其析构函数释放需赋值对象的原来的字符串空间。整个函数不需要显式用到 new、delete,内存的分配和释放都自动完成,因此代码显得比较优雅。