在operator=中处理自我赋值(Effective C++_11)

一、我们平时习惯写的赋值操作代码
我们平时可能已经习惯了这样的代码,并且认为是对的:

#include<iostream>
using namespace std;

class Demo{
public:
    Demo& operator=(const Demo& x);
    Demo(int x=10,int y=15):a(x),b(y){
        p=new int;
    }
    void Print(){
        cout<<"a="<<a<<endl;
        cout<<"b="<<b<<endl;
    }
private:
    int a;
    int b;
    int* p;
};

Demo& Demo::operator=(const Demo& x){
    a=x.a;
    b=x.b;
    delete p;
    p=new int(*(x.p));
    return *this;

}

int main(){
    Demo obj(40,50);
    Demo obj2;
    obj2=obj;
    obj.Print ();
    obj2.Print();
    return 0;

}

二、存在的问题
确实,以上代码运行能通过,赋值操作也能完美运行。但是,只是表面上看起来合理而已,当出现自我赋值时并不安全(实际上,也不具备异常安全性)。上面代码中,有可能*this和x是同一对象,假如真的如此,delete不仅仅删除了当前对象的Demo,也删除了x的Demo,这样,这函数末尾,持有的指针会指向一个已经删除的对象,(注:但是用obj=obj时,也能运行通过,不知道为什么??,先按书中的来吧)


三、解决方法
为了解决这种错误,我们需要这样做:

Demo& Demo::operator=(const Demo& x){
    if(this==&x)//从地址上判断是否指向同一块内存
        return *this;
    a=x.a;
    b=x.b;
    delete p;
    p=new int(*(x.p));
    return *this;

}

上述方法确实能解决自我赋值问题,但是还不具备异常安全性,假设p=new int(*(x.p))出现异常,那么最终会导致Demo持有一个指针指向被删除的一块Int,为了解决异常问题,可以采取下面的策略:

Demo& Demo::operator=(const Demo& x){
    if(this==&x)
        return *this;//可以省略这段防止自我赋值的代码
    a=x.a;
    b=x.b;
    int* temp=p;//保存旧指针
    p=new int(*(x.p));//如果new操作发生异常,则仍然指向原来的空间
    delete temp;//删除原先的temp
    return *this;

}

注意上述代码中即使没有

if(this==&x)
        return *this;

也是行的通的,因为我们对原来的指针做了一个备份,删除旧的,指向新的;


四、最优方法
解决自我赋值和异常问题还有两个版本,即使用所谓的copy and swap技术

Demo& operator= (const Demo& x)
 {
          Demo temp(x);
          swap(*this, temp);
          return *this;
 }

copy and swap的原理:为你打算修改的对象做出一份副本,然后在那份副本身上做出一切修改,若有任何修改动作抛出异常,原对象仍然保持未修改的状态;待所有改变都成功后,再将修改过的那一个副本和原对象在一个抛出异常给的操作中置换(swap)(具体参见条款29);这里把所有负担都落在了复制构造函数上,如果出现异常,那么swap就不会执行;

另一个版本是

Demo& operator= (const Demo x)
 {
          swap(*this, temp);
          return *this;
 }

注意,这个版本和上面的有所不同,参数是通过传值,而传值操作会生成一份复件,即此时的x是原对象的一份副本(会调用赋值构造函数)(传值方式请参考条款20),那么申请临时变量的任务就落在形参上了;这样的方式牺牲了代码的清晰性,而增加了代码的高效;


参考:Effective C++ 3rd(侯捷译)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值