标签:
类重载赋值操作符一般都是作为成员函数而存在的,那函数应该返回什么类型呢?参考内置类型的赋值操作,例如
int x,y,z;
x=y=z=15;
赋值行为相当于x=(y=(z=15)),也就是赋值操作应该返回左操作数的引用,因此,为了和内置类型兼容,类中重载赋值操作符应该返回左操作数的引用,即*this,如下类A的重载赋值操作函数的声明,
class A{};
A& A::operator=(const A&);
对于赋值操作,首先应该想到的是怎么处理自我赋值,当类包含指针类型的数据时尤为重要,如下所示
class MyString{
public:
...
MyString(char *p=NULL);
MyString& operator=(const MyString&);
private:
char *str;
};
MyString a("hello");
MyString b("world");
a=a;
我们知道,进行赋值时,首先要释放左操作数的资源,然后再根据右操作数对左操作数进行赋值,赋值操作函数如下所示
MyString& MyString::operator=(const MyString& rhs){
delete [] str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
return *this;
}
当运行a=a时,会产生意向不到的后果,我们在函数中先释放了左操作数的资源,然后访问右操作数的资源,然后当自我赋值发生时,this==&rhs,也就是说我们先释放了资源,然后又访问了已经被释放的资源的内容,这显然会引起程序崩溃,所以我们需要进行是否是自我赋值操作的验证,修改过后的函数如下
MyString& MyString::operator=(const MyString& rhs){
if(this==&rhs)
return *this;
delete [] str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
return *this;
}
此时,函数虽然处理了自我赋值,但是仍然存在问题,如果new操作失败了怎么办?
我们已经释放了左操作数的资源,但是在重新分配资源时由于空间不够等原因失败了,如果我们继续对已经释放了的资源进行访问,会产生未定义的结果,赋值操作就不具备异常安全性,对于这个问题有两种解决方案
第一,调整语句顺序,先保存原来的资源,等重新分配资源完成以后再释放以前的资源
MyString& MyString::operator=(const MyString& rhs){
char *pTemp=str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
delete [] pTemp;
return *this;
}
这段代码也能够处理自我赋值,但是必须执行完函数中的所有复制、分配和释放操作,如果自我赋值发生的概率比较高,我们也可以将测同语句放进该函数。
第二、采用copy and swap技术
MyString& MyString::operator=(const MyString& rhs){
if(this!=&rhs){
MyString temp(rhs);
char *pTemp=str;
str=temp.str;
temp.str=pTemp;
}
return *this;
}
这种技术赋值的是指针而不是为对象重新分配资源,我们在if语句内重新构造了一个新的临时对象,然后将临时对象的str和本类对象的str进行交换,当程序执行到if语句外时,临时对象自动调用析构函数,释放自己的资源,此时临时对象所持有的资源就是原来this所持有的资源,该资源得以释放,而现在this所持有的资源是rhs所持有的资源,即此时rhs和this中的str所指向的是同一块空间。
对于有继承关系的类,在对其派生类编写复制控制的函数时,由于派生类无法访问基类中的私有成员,所以在派生类的重载操作符函数中,需要首先调用其基类的赋值操作符函数对派生类中的基类成员进行赋值,然后再对派生类中的特有成员进行赋值。
标签:
原文地址:http://www.cnblogs.com/sjinsa/p/4590281.html