01 赋值运算符函数
题目
为类型CMyString编写赋值运算符函数。
class CMyString{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
~CMyString();
private:
char* m_pData;
};
C++ 题解
- 为了支持连续赋值的操作,返回值类型应该是引用类型
- 在类中返回对象自身引用的方法是返回 *this
- 当函数的参数不可变时应当为参数添加const关键字
- 函数参数是类对象时应该传入引用而非实例,如果传进去的是实例将会调用一次构造函数,代码的效率降低
方法一
CMyString & CMyString::operator = (const CMyString &str)
{
if (this == &str)
return *this;
delete[] m_pData;
m_pData = nullptr;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
注意:
- 必须判断当前实例(*this)和传入的参数是否是同一个对象,如果是则直接返回,如果每天判断,那么下面的释放操作(delete[] m_pData;)相当于把传入的参数也给释放了。
- 在分配新的内存前需要先释放之前已有的空间,否则造成内存泄漏。
方法2
方法1的不足是我们在为m_pData 分配新的内存之前,先将m_pData 释放掉了,如果内存不足导致new char抛出异常, m_pData将是一个空指针,这样很容易导致程序崩溃。也就是说一旦在赋值运算符函数内抛出一个异常, CMyString的实例不再保持有效的状态,这就违背了异常安全性原则(不泄漏任何资源、不破坏数据)。
更优化的方法:
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;
}
优点:
- strTemp 是一个局部变量,程序运行到if语句之外时,它的生命周期就结束,会自动调用析构函数释放内存strTemp.m_pData,而strTemp.m_pData 现在所指向的内存与当前实例的m_pData 内存是一致的,这样就避免了内存的泄漏。
- 如果在创建strTemp的构造函数因为内存不足抛出异常,这时当前实例的状态并没有改变,从而保证了异常安全性。