本文为博主原创文章,未经博主允许不得转载 http://www.cnblogs.com/kiplove/p/6783497.html
C++的重载运算符,由关键字operator和运算符号共同组成,一般而言C++里只要运算符不含"."都可以重载。
这里主要讲一下赋值运算符的重载。
一、一个重载赋值运算符的的例子
class Mystring { public: // Mystring()=default; Mystring(string pData="null") { m_pData=new string; *m_pData=pData; } Mystring(const Mystring &str)
{
m_pData=new string;
*m_pData=*str.m_pData;
}
~Mystring(void){delete m_pData;} Mystring& operator=(const Mystring &ptr) { if(this==&ptr) return *this; delete m_pData; m_pData=NULL; m_pData=new string; *m_pData=*ptr.m_pData; return *this; } string print(){return *m_pData;} private: string *m_pData; }; int main() { Mystring a("hello"); Mystring b,c; b=c=a; // (b=c)=a; cout<<a.print()<<b.print()<<c.print()<<endl; return 0; }
二、分析
1、返回值类型
返回类型一般声明为类型的引用,并在函数结尾时返回实例自身的引用(即*this)。这里主要有两个原因:
a.返回引用可以减少一次拷贝构造和析构函数导致不必要的开销,因为返回值类型不是引用,会创建一个匿名对象,这个匿名对象时个右值,获取return的值。
b.可以实现连续赋值
在例子中
b=c=a;
返回值不是引用类型也是可以的,其运算顺序
b=(c=a);
c得到一个右值,再将右值赋给b,所以逻辑上没有问题的。但是如果是
(b=c)=a;
这里将会出错,b=c后b是一个右值,所以b=a时将会出错。
2、参数
参数声明为const且是一个引用
a.const 是因为赋值运算,不希望修改原来类的状态,同时可以接受const与非const的参数
b.引用则避免了拷贝构造函数
3、判断是否是传入实例与当前实例是同一个(证同测试),保证自赋值的安全
如果相同,直接返回可以减少不必要的操作
同时防止指向的同一资源一起被销毁。
class Widget{ …… private: Bitmap: pb; } Widget& Widget::operator=(const Widget& rhs) { delete pb; pb=new Bitmap(*rhs.pb); return *this; } //当出现*this与rhs是同一对象时会出现指向对象被删除
4、赋值前,释放自身的内存
三、考虑异常安全性
异常安全函数保证:
a.不泄露任何资源
b.不允许数据破坏
对于第一点一般可以用资源管理类可以实现,采用智能指针,综合两点保证可以用通过copy-and-swap实现异常安全。
Mystring& operator=(const Mystring &ptr) { if(this!=&ptr) { Mystring temp(ptr); string ptemp=*temp.m_pData; *temp.m_pData=*m_pData; *m_pData=ptemp; } return *this; }