《剑指offer》面试题1:赋值运算符函数 思考总结

题目

在这里插入图片描述

注意事项

对于本题,正如书中所讲我们需要注意以下几点:

  • 考虑返回值的类型:只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是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;
}

当我们考虑到异常安全性时:上述代码又会有一些其他问题。
这里我们先说明一下什么是异常安全性:

相关知识点

异常安全性

一个函数如果说是“异常安全”的,必须同时满足以下两个条件:

  1. 不泄漏任何资源;
  2. 不允许破坏数据。

C++中’异常安全函数”提供了三种安全等级:

  1. 基本承诺:如果异常被抛出,对象内的任何成员仍然能保持有效状态,没有数据的破坏及资源泄漏。但对象的现实状态是不可估计的,即不一定是调用前的状态,但至少保证符合对象正常的要求。

  2. 强烈保证:如果异常被抛出,对象的状态保持不变。即如果调用成功,则完全成功;如果调用失败,则对象依然是调用前的状态。

  3. 不抛异常保证:函数承诺不会抛出任何异常。一般内置类型的所有操作都有不抛异常的保证。

如果一个函数不能提供上述保证之一,则不具备异常安全性。

对于第一个安全等级”基本承诺“,可以通过资源管理对象智能指针来实现;
对于第二个安全等级,我们可以通过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)
{
    //异常处理
    //弹出提示对话框
    // 返回
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mi ronin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值