题目
注意事项
对于本题,正如书中所讲我们需要注意以下几点:
- 考虑返回值的类型:只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是void,则应用该赋值运算符将不能进行连续赋值。
简单理解我们可以这样解释:因为此处赋值运算符是左值,所以就是引用返回
(详细解释见: C++运算符重载-返回类型) - 传入的参数类型声明为常引用。
因为如果我们传入的参数不是引用而是实例,那么从形参到实参会调用一次拷贝构造函数。
并且我们在赋值运算符函数内不会改变传入的实例状态,因此应该为传入的引用参数加上const关键字。 - 创建新内存空间的时候 需要释放掉释放实例自身已有的内存。
如果我们忘记在分配新内存之前释放已有的空间,则程序将出现内存泄漏的问题。 - 判断传入的参数和当前的实例(* this)是不是同一个实例。
如果是同一个对象那么就直接返回即可。如果事先不判断就进行赋值,那么在释放实例自身内存的时候就会导致严重的问题:当*this和传入的参数是同一个实例时,一旦释放了自身内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。
根据以上四点注意事项,代码如下:
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]; /*注意“\0”*/
//将内容拷贝过来
strcpy_s(m_pData,strlen(str.m_pData)+1,str.m_pData);
//返回一个对象
return *this;
}
当我们考虑到异常安全性时:上述代码又会有一些其他问题。
这里我们先说明一下什么是异常安全性:
相关知识点
异常安全性
一个函数如果说是“异常安全”的,必须同时满足以下两个条件:
- 不泄漏任何资源;
- 不允许破坏数据。
C++中’异常安全函数”提供了三种安全等级:
-
基本承诺:如果异常被抛出,对象内的任何成员仍然能保持有效状态,没有数据的破坏及资源泄漏。但对象的现实状态是不可估计的,即不一定是调用前的状态,但至少保证符合对象正常的要求。
-
强烈保证:如果异常被抛出,对象的状态保持不变。即如果调用成功,则完全成功;如果调用失败,则对象依然是调用前的状态。
-
不抛异常保证:函数承诺不会抛出任何异常。一般内置类型的所有操作都有不抛异常的保证。
扫描二维码关注公众号,回复: 14575119 查看本文章
如果一个函数不能提供上述保证之一,则不具备异常安全性。
对于第一个安全等级”基本承诺“,可以通过资源管理对象和智能指针来实现;
对于第二个安全等级,我们可以通过copy and swap的策略来实现!
其原理很简单:即先对打算修改的对象做出一个副本(copy),在副本上做必要的修改。如果出现任何异常,原对象依然能保证不变。如果修改成功,则通过不抛出任何异常的swap函数将副本和原对象进行交换(swap)。
而对于我们本道面试题我们就采用copy and swap的策略来实现。
代码如下:
CMyString &operator = (const CMyString &str)
{
if(this!=&str)
{
CMyString strTemp(str); //copy过程
char *pTemp = strTemp.m_pData; //swap过程
strTemp.m_Data = m_pData;
m_pData = pTemp;
}
return *this;
}
对于上述代码,我们先创建了一个临时对象strTemp,接着把strTemp.m_pData和对象自身的m_pData进行交换。
由于strTemp是一个局部变量,但程序运行到if的外面时也就出了该变量的作用域,就会自动调用strTemp的析构函数,把strTemp.m pData所指向的内存释放掉。由于strTemp.m_pData指向的内存就是实例之前m pData 的内存,这就相当于自动调用析构函数释放实例的内存。
在新的代码中,我们在CMyString 的构造函数里用new 分配内存。如果由于内存不足抛出诸如 bad_alloc等异常,我们还没有修改原来实例的状态,因此实例的状态还是有效的,这也就保证了异常安全性。
下面对bad_alloc异常进行说明
bad_alloc
1、举例
在对一个120w行的数据进行解析的时候,程序运行时出现了崩溃,系统提示出现了std::bad_alloc异常。
已知:在使用new分配内存空间时,内存空间不够时就会抛出该异常。
解决:在程序中加入delete和clear及时释放内存,并且对原始数据进行分割处理。
2、异常处理
当分配较大块内存时,进行内存分配失败的异常处理。避免程序的运行错误或崩溃。
利用try-catch模块函数,将内存分配语句放在try中,这样出现了异常就会立刻获得,从而转入匹配的catch块进行处理。catch的参数是异常类型,这里为std::bad_alloc。
int *a;
try
{
//分配内存
a= new int[bigBigNum];
}
catch(std::bad_alloc)
{
//异常处理
//弹出提示对话框
// 返回
}