01 赋值运算符函数

题目

为类型CMyString编写赋值运算符函数。

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

C++ 题解

  1. 为了支持连续赋值的操作,返回值类型应该是引用类型
  2. 在类中返回对象自身引用的方法是返回 *this
  3. 当函数的参数不可变时应当为参数添加const关键字
  4. 函数参数是类对象时应该传入引用而非实例,如果传进去的是实例将会调用一次构造函数,代码的效率降低

方法一

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;
}

注意:

  1. 必须判断当前实例(*this)和传入的参数是否是同一个对象,如果是则直接返回,如果每天判断,那么下面的释放操作(delete[] m_pData;)相当于把传入的参数也给释放了。
  2. 在分配新的内存前需要先释放之前已有的空间,否则造成内存泄漏。

方法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;
}

优点:

  1. strTemp 是一个局部变量,程序运行到if语句之外时,它的生命周期就结束,会自动调用析构函数释放内存strTemp.m_pData,而strTemp.m_pData 现在所指向的内存与当前实例的m_pData 内存是一致的,这样就避免了内存的泄漏。
  2. 如果在创建strTemp的构造函数因为内存不足抛出异常,这时当前实例的状态并没有改变,从而保证了异常安全性。
posted @ 2019-01-19 20:47  youngliu91  阅读(257)  评论(0编辑  收藏  举报