【C++基础】----操作符重载(03)

今天我们来讨论操作符重载中比较重要的一个内容:赋值操作符的重载

1. 常量不允许出现在=左边

由于编译期对赋值有着严格的要求和限制,因此强制 operator=() 为成员函数。

成员函数的优点在于在调用时,永远有一个隐式的this指针被调用,而反观友元函数,我们可以将任何两个对象传给友元函数,在下面的例子中,我们会看到这将是一个很大的隐患。

如果我们使用了友元函数进行重载,那很有可能出现诸如

2 = 5 ;

之类的疯狂语句,强制规定的根本原因是在友元函数的情况下用户就可以打破C++的规定,使常量出现在等号的左边。

2. 如何禁止自赋值

创建一个operator=()时,必须从右侧对象中拷贝所有需要的信息到当前对象,已完成对象的赋值。

#include <iostream>
using namespace std ;

class Value{
        int a_ , b_ ;
        float c_ ;
public :
        Value(int a , int b , float c) : a_(a) , b_(b) , c_(c) {}
        Value() : a_(0) , b_(0) , c_(0.0) {}
        Value operator=(const Value& obj) {
                a_ = obj.a_ ;
                b_ = obj.b_ ;
                c_ = obj.c_ ;
                return *this ;
        }
        friend ostream& operator<<(ostream& os , const Value& obj) ;
};

ostream& operator<<(ostream& os , const Value& obj) {
        return os << "a=" << obj.a_ << ", b=" << obj.b_ << ", c=" << obj.c_ << endl ;
}

int main(void) {
        Value a , b(1 , 2 , 3.1) ;
        a = b ;
        cout << a ;
        return 0 ;
}


其中有一个十分隐蔽的错误,就是自赋值的问题,我们有可能让一个对象调用operator= 时传进的参数也是自己,这样实际上就会自己给自己赋值。在C++中这是一个很严重的错误,有可能出现在程序结束调用析构函数时,同一块内存空间被释放两次的可怕后果。

因此,应记住一个常识:当我们准备给两个相同类型的对象赋值时,先检查这个对象是否在对自己进行赋值,如果我们不进行检查,就可能产生难以发现的错误。

经过修改的重载函数应该这样去实现

class Value{
        int a_ , b_ ;
        float c_ ;
public :
        Value(int a , int b , float c) : a_(a) , b_(b) , c_(c) {}
        Value() : a_(0) , b_(0) , c_(0.0) {}
        Value operator=(const Value& obj) {
                if(& obj == this)               //if there two objects are identity
                        return *this ;          //we should shutdown this function
                a_ = obj.a_ ;
                b_ = obj.b_ ;
                c_ = obj.c_ ;
                return *this ;
        }
        friend ostream& operator<<(ostream& os , const Value& obj) ;
};



3. 类中的指针

如果对象中包含指向其他对象的指针,问题会更加复杂。

这就涉及了深拷贝和浅拷贝的区别。

因为我们如果仅仅使用之前一直介绍的浅拷贝的话,在赋值过程中,A对象指针成员所指向的对象的地址会原封不动的赋值给B对象,两个对象会共同指向同一对象



如果我们使用上面的类,就会出现问题

为了方便起见,画图给大家描述一下


如果只是用简单的浅拷贝,就会使得它们指向同一对象。

因此,我们在实现时,在指针的赋值时,不应该只是简单的赋值,应该创建一个与原对象指针指向对象相同的新对象,再用被赋值对象的指针去指向,才能避免。



class CA
{
public:
char* p;
   CA(){p = NULL;};
void Set(char* pStr)
{
    delete []p;
if(pStr == NULL)
{
         p = NULL;
}
else
{
p = new char[strlen(pStr)+1];
        strcpy(p, pStr);
}
};
    CA& operator=(CA& a)
{
cout<<” operator = invoked/n”<<endl;
    //没有检测自赋值情况
    delete []p;
    p = a.p;
   a.p = NULL;
   return *this;
};
    ~CA(){delete []p;};
};


CA对象“拥有”它成员p指向的内存。所以,在赋值函数中,参数a将放弃 它的“拥有权”,并将它转交给调用对象。(C++标志库中定义的智能指针auto_ptr就是一种“拥有”型智能指针,它也存在这种“拥有权转移”的性质)
请见下面的例子代码(例子代码1):
CA a1, a2;
a1.Set(“Ok”);
a2 = a1;
我们的函数看起来工作的很好,但是,请看下面一条语句:
a2 = a2;// 悲剧发生了,a2“拥有”的内存被释放了!
所以,赋值运算符函数应写为下面的形式:

CA& CA::operator=(CA& a)
{
   cout<<” operator = invoked/n”<<endl;
//检测自赋值情况
if(this != &a)
{
delete []p;
        p = a.p;
    a.p = NULL;
}
return *this;
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值