题目:如下为类型 CMyString 的声明,请为该类型添加赋值运算符函数。
类的声明如下:
1 class CMyString 2 { 3 public: 4 CMyString(char *pData = NULL); 5 CMyString(const CMyString &str); 6 ~CMyString(); 7 8 private: 9 char *m_pData; 10 };
关于赋值运算符函数,我们应该从以下几个方面考虑:
1、返回值:首先我们应该把返回值的类型声明为该类型的引用,并在函数结束前返回本身的引用。只有返回一个引用才能允许连续赋值,如 str1 = str2 = str3;
2、参数:其次我们应该把传入的参数的类型声明为常量引用。
传入的引用是为了避免从形参到实参调用的一次复制构造函数;这也就是传值与传引用的效率问题。
传入的为常量从语义的角度出发,我们在赋值运算函数内并不希望改变传入的实例的状态,因此,应该为传入的引用参数加上 const 关键字。
3、内存:我们需要实例释放自身已有的内存,如果我们王子在分配新内存之前释放掉自身已有的内存,就会造成内存泄露;
4、自身赋值问题:最后我们需要判断传入的参数和当前的实例(*this)是否为同一个实例。如果为同一个,则不进行赋值操作。因为,赋值之前需要释放已有的内存,如果为同一实例,那么传入的参数的内存在当前实例释放内存的同时也被释放了,这样就无法找到需要赋值的内容了。
一、基本的解法:
1、判断传入的参数是否为当前的实例本身;
2、就是释放掉当前实例(*this)的内存;
3、进行赋值;
4、返回实例(*this);
1 CMyString& CMyString::operator=(const CMyString &str) 2 { 3 if(this == &str) 4 return *this; 5 6 delete []m_pData; 7 m_pData = NULL; 8 m_pData = new char[strlen(str.m_pData)+1]; 9 strcpy(m_pData, str.m_pData); 10 11 return *this; 12 }
二、考虑异常安全性的解法:
解法一存在安全问题,考虑如下问题:
我们在分配内存之前先用 delete 释放了实例 m_pData 的内存。 如果此时内存不足导致 new char 抛出异常,这样 m_pData 将会是一个空指针,这样非常容易导致程序崩溃。
在这里,我们用借用一个临时变量来解决上述问题。具体步骤为:
先用传入的参数创建一个临时变量tmp,接着用该临时变量tmp 与实例自身的 m_pData 数据做交换。由于tmp为一个局部变量,该段程序运行结束时就是自动调用tmp的析构函数,把tmp的内存释放掉。
代码如下:
1 CMyString& CMyString::operator=(const CMyString &str) 2 { 3 if(this != &str) 4 { 5 CMyString tmp(str); 6 7 char *pTmp = tmp.m_pData; 8 tmp.m_pData = m_pData; 9 m_pData = pTmp; 10 } 11 return *this; 12 }
三、完整的CMyString类的实现及测试代码:
CMyString.hpp文件
1 #ifndef CMYSTRING 2 #define CMYSTRING 3 4 #include <string.h> 5 6 class CMyString 7 { 8 public: 9 CMyString(); 10 CMyString(char *pData); 11 CMyString(const CMyString &str); 12 CMyString& operator=(const CMyString &str); 13 ~CMyString(); 14 15 private: 16 char *m_pData; 17 }; 18 19 CMyString::CMyString() 20 :m_pData(new char[1]) 21 { 22 m_pData = '