在下面类中实现:
class CMyString
{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
CMyString& operator=(const CMyString& str);
~CMyString(void);
private:
char * m_pData;
};
为了写出一个完整的赋值运算符重载函数,我们要考虑以下几个问题
- 是否把返回值的类型声明为该类类型的引用,并在函数结束之前返回自身的引用(*this)。只有返回一个引用,才可以连续赋值,同时在赋值过程中不会改变传入实例的状态,加上const关键字
- 是否释放实例自身已有的内存。如果我们忘记在分配新内存之前释放自身已有的内存,程序会出现内存泄漏
- 是否判断传入的参数和当前实例是不是同一个实例。如果事先不判断就进行赋值,那么在释放自身的内存中将会导致严重的问题,当*this和传入的参数是同一个实例时,那么一旦释放自身的内存,传入参数的内存也将会被释放,找不到需要赋值的内容了
一步步解决上述的问题,我们可以编写出经典的算法
CMyString& CMyString::operator=(const CMyString& str)
{
if(this != &str)
{
delete[] this->m_pData;
this->m_pData = NULL;
this->m_pData = new char[strlen(str.m_pData)+1];
strcpy(this->m_pData,str);
}
return *this;
}
考虑到异常安全性的解法
在前面的函数中,我们在分配内存之前就先释放掉了实例的m_pData的内存。如果此时内存不足导致new char异常,m_pData将会是一个空指针,这样程序容易崩溃。
解决办法:创建一个临时实例,再交换临时实例和原来的实例
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;
}