题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。
class CMyString{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
~CMyString(void);
private:
char* m_pData;
}
经典解法,适合初级程序员:
CMyString& CMyString::operator = (const CMyString &str){
/*返回值为引用,才可以允许连续赋值(str1 = str2 = str3)。
传入参数为常引用,能避免参数是实例时从形参到实参调用复制构造函数的消耗,且在函数内不会改变传入实例的状态。*/
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;
}
上述代码存在的问题:
在分配内存前用delete释放了实例 m_pData 的内存。如果此时内存不足导致 new char 抛出异常, m_pData 将是一个空指针, CMyString 的实例不再保持有效的状态(已被释放),违反异常安全性原则。
解决办法:
先用new分配新内容,再用delete释放已有内容。这样只在分配内容成功后再释放原来的内容,也就是当分配内存失败时能保证CMyString的实力不会被修改。
- 先创建一个临时实例,再交换临时实例和原来的实例。如下所示。由于 strTemp 是一个局部变量,程序运行到if的外面时就出了该变量的作用域,就会自动调用 strTemp 的析构函数,把 strTemp.m_pData 所指向的内存释放掉。由于 strTemp.m_pData 指向的内存就是实例之前 m_pData 的内存,这就相当于自动调用析构函数释放实例的内存。我们在 CMyString 的构造函数里用 new 分配内存,如果由于内存不足抛出诸如 bad_alloc 等异常,我们还没有修改原来实例的状态,因此实例的状态还是有效的,这也就保证了异常安全性。
CMyString& CMyString::operator = (const CMyString &str){
if (this != str){
CMyString strTemp(str);
char* pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}
测试用例:
- 把一个CMyString的实例赋值给另外一个实例。
- 把一个CMyString的实例赋值给它自己。
- 连续赋值。