比如以下是CMyString的声明,请为该类添加赋值操作符
Class CMyString
{
Public:
CMyString(char *pDatra = nullptr);
CMyString(constCMyString &str);
~CMyString(void);
Private:
Char * m_pData;
};
为这个函数重载一个赋值操作符
在为这个类重载赋值操作符的时候首先需要注意一下几点:
1) 返回值的类型得是该类的引用,并在函数结束前返回实例自身的引用(*this),只有这样连续赋值才可以通过,比如有三个CMyString对象,str1,str2,str3,str1=str2=str3必须返回引用才可以通过,至于为何要返回自身的引用是因为,假如返回对象,会再次调用一次拷贝构造函数,这就造成了多于的时间
2) 要把传入的参数设置为常量的引用,如果传入的参数不是引用而是实例,那么传入的参数又会调用一次拷贝构造函数,把参数声明为引用可以避免这样的无谓的消耗,同时传入的参数应该是不能修改,所以改为const
3) 实例自身有一个Char *指针,在使用新的实例给他赋值的时候需要把它释放掉,要不然会出现内存泄露
4) 这一点可能很难想到,需要判断传入的的实例是不是其本身,因为假如传入的实例是其本身的话,将char *释放掉之后在用其赋值会导致乱码,程序奔溃,
如何判断某个实例是否是其本身呢,这样判断是不对的,if(str==*this),这样是判断两个实例是否相等,不能判断两个实例是否是一个实例,而应该if(&str==this)用地址来判断才对
经典的写法,适用于初级程序员:
CMyString&CMyString::operator = (const CMyString &str)
{
If(this==&str)return *this;
Delete []m_pData;
m_pData =nullptr;
m_pData = newchar[strlen(str.m_pData)+1];
strcpy(m_pData,str.m_pData);
return *this;
}
在这个写法中,使用delete释放了指针的空间,如果申请新的空间,如果此时内存不足导致new char抛出异常,则m_pData将是一个空指针,这样非常容易导致程序崩溃,这就违背了异常安全性的原则,当抛出异常的时候应该保证数据不被修改,程序不会崩溃、
要想在赋值运算中实现异常安全性,我们有两种方法,一种简单的方法是我们先用new分配新的内存,再用delete释放已有的内容
第二种方法即先创建一个临时实例,在交换临时实例和原来的实例,下面是这种思路的参考代码
CMyString&CMyString :: operator = (const CMyString &str)
{
If(this!=&str)
{
CMyStringstrTemp(str);
Char * pTemp =strTemp.m_pData;
strTemp.m_pData= m_pData;
m_PData = pTemp;
}
Return *this;
}
如果pTemp声明成功, 会交换this和pTemp的内容,然后会调用pTemp的析构函数把原来的内容释放掉,
如果声明失败,原来的类的实例不会发生改变,这样就维护了异常安全性