【剑指offer】一、赋值运算符函数

时间:2022-03-09 19:50:26

题目:

如下为类型CMyString的声明,请为该类型添加赋值运算符函数:

class CMyString
{
public:
CMyString(char* p_Data = NULL);
CMyString(const CMyString& str);
~CMyString(void);
private:
char* m_pData;
};

题目解析:

1、拿到一个题目时,我们应该先理清大概的思路,不要着急直接写代码。思考可能会发生的一些场景,怎样处理这些场景。以及考虑到一些极端情况。

2、赋值运算符函数的注意事项:

  • 是否把返回值的类型声明为该类型的引用,并在函数结束前返回该类型的引用(即*this)。只有返回一个引用,才会允许连续赋值。
  • 是否把传入的参数类型声明为常量引用。如果传入的参数不是引用而是实例的话,就得再调用一次复制构造函数,降低了代码的效率。
  • 是否释放实例自己已有的内存。如果没有在分配新内存之前释放原有内存,就会造成内存泄漏。
  • 是否判断传入的参数与当前的实例是不是同一个实例。

普通解法:

CMyString& CMyString::operator=(const CMyString& str){
if (this != &str){
//释放原有内存
delete[] m_pData;
m_pData = NULL;
//分配新内存
m_pData = new char(strlen(str.m_pData) + 1);
strcpy(m_pData, str.m_pData);
}
return *this;
}

这是普通的解法,上面所说的赋值运算符函数的注意事项,我们也都符合要求。但是我们还是要想想一些极端情况这个函数能处理吗?比如在这个函数里我们是先释放内存,再分配新内存。可是如果在释放了当前对象的原有内存后,给当前内存分配新内存时因为内存不足而失败,抛出异常而退出时。这时问题就来了,当前对象的原有空间释放掉了,新空间也申请失败,当前对象的指针变成了一个野指针,这时代码就出问题了,程序会崩溃。那我们要如何改进呢?这时我们可以借助临时变量来实现,也就是我们升级版的函数。


考虑异常安全性的解法:

我们可以先创建一个临时实例,在交换临时实例和原来的实例。因为该临时实例是一个局部变量,所以在出了作用域之后,系统会自动调用该类的析构函数,释放临时变量所指向的空间。代码如下:

CMyString& CMyString::operator=(const CMyString& str){
if (this != &str){
//创建临时变量
CMyString Temp(str);
char* pTemp = Temp.m_pData;
Temp.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}