为一个类型添加赋值运算符,需要考虑的问题有很多,特别是一些细节的问题:例如参数类型,返回类型,异常安全性等等。
/*赋值运算符函数
题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数
*/
class CMyString{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString & str);
~CMyString(void);
private:
char* m_pData;
};
/*赋值运算符函数首先需要考虑以下几点:
1、是否将返回值的类型声明为该类型的引用,并在函数结束前返回实参自身的引用(即*this)。
只有返回一个引用,才可以允许连续赋值
否则如果函数的返回是void,将不能做连续赋值,即类似于str1=str2=str3将不能通过编译。
2、 是否将传入的参数的类型声明为常量引用。
如果传入的是实参而不是引用,那么从形参到实参将会调用一次复制构造函数。这样会增大无谓的消耗。
赋值运算符不会改变传入的实例的状态,所以应该为传入的引用的参数加上const关键字
3、是否释放实例自身已有的内存。
如果在分配新内存之前释放自身已有的空间,程序会出现内存泄露
4、是否判断传入的参数和当前的实例(*this)是不是同一个实例 ,类似于(a=a)
如果是,则不需要进行赋值操作,直接返回。
如果是,一旦释放自身的内存时,传入的参数的内存也同时被释放,就会找不到需要赋值的内容了。
*/
CMyString& CMyString::operator =(const CMyString& str) //注意返回类型,传入参数的类型
{
if(this == &str) //判断this与传入的参数是否是同一个
return *this;
delete []m_pData; //释放已有内存
m_pData = NULL; //将其作为一个空指针
m_pData = new char[strlen(str.m_pData)+1]; //分配空间,多一个是用于存放字符串结束符'\0'
strcpy(m_pData,str.m_pData); //复制
return *this;
}
//上述程序仍然存在一些问题
//当你释放了实例m_pData的内存时,如果此时内存不足会导致new char跑出异常,m_pData会是一个空指针,这样很容易导致程序崩溃。违背了异常安全性原则。
//两种解决方案:1、先分配后释放,只能保证分配失败时CMyString实例不会被修改; 2、先创建一个临时实例,再交换临时实例与原来的实例
CMyString& CMyString::operator =(const CMyString& str) //注意返回类型,传入参数的类型
{
if(this!=&str){
CMyString strTemp(str); //新建一个实例
//交换strTemp.m_pData 和实例自身的m_pData
char* pTemp=strTemp.m_pData;
strtemp.m_pData=m_pData;
m_pata=pTemp;
}
return *this;
}
//由于strTemp是一个局部变量,出了作用域就会自动调用析构函数,把strTemp.m_pData所指向的内存释放掉。交换后,它所指向的就是实例之前的内存。
参考资料:《剑指offer》