C++ 重载赋值操作符operator=的高级议题


C++ class perator= 重载赋值操作符。


关于重载赋值操作符的例子网上已经是一搜一大把了,在这里我就不做这些介绍了,只给大家总结一下对于这个操作符的基本注意事项。

1.        首先在函数中做

if(this == & rhs);

//判断是否是自我赋值;

2.        返回值类型为T& 不可为const T&。

3.        返回值为*this ,满足 a=b=c;

4.        传入参数为 const T&。

5.        添加成员变量之后要重写operator=。

6.        operator= 函数内部可以直接调用传入参数的私有变量。

好了,以上都是一些基本的注意事项。下面让我们来讨论一下在operator=的高级议题吧。

在哪种情况下需要operator=

你需要改变defaultoperator =的语意时,就要重载operator=; default operator=的语意是bitwiseassignment;

两种情况通常需要overloadoperator=:

1.class 内有pointer or reference;

2.class 是derived class,或class 有 composite member objects.

一般情况下,编译器自动完成的赋值操作符就够用了,也能出色的完成我们给出的任务。在c++的教科书上说如果类中存在指针就需要重写赋值操作符。这样说也很正确,避免两个指针指向同一段内存,在释放的时候重复释放。这个时候需要重新开辟一块大小相同的内存拷贝一份内容。

现在有一种情况是该指针本来不属于类对象本身,指向的也是一块公用的内存地址,内容的所有权不归当前对象所有。比如 const char*。只对这块内存有读的权利,同时这个内存非常大,每次拷贝起来都很费时间。这种情况下就不需要拷贝内存中的内容,只要保证在析构函数中不去释放这块内存即可。

 

这块内存区域就需要在类对象外部管理,如何申请如何管理如何释放。

 

重载赋值操作符operator=,拷贝构造函数T(const T& t)可否互相调用

首先明确:拷贝构造函数的存在意义是通过已有的对象构造新的对象,构造完毕后才有两个对象;重载赋值操作符的意义在于将一个对象的值赋给另一个对象,两个对象都已经构造完毕了。拷贝构造函数调用重载赋值操作符:把已有对象的值赋给一个构造中的对象,虽然这个对象的内存已经分配好了。(可以接受,但是有可能导致循环调用重载赋值操作符和拷贝构造函数)

 

例子1:拷贝构造函数调用重载赋值操作符(导致循环调用重载赋值操作符和拷贝构造函数)

#include<iostream>
usingnamespace std;
 
classBase {
public:
         Base() {cout << "Constructorinvoked!" << endl;}
         ~Base() {cout << "Destructorinvoked!" << endl;}
         Base(const Base& rhs) {
                   cout << "Copyconstructor invoked!" << endl;
                   operator=(rhs); // *this =rhs;
         }
         Base operator=(const Base& rhs) {// 问题出在这里,返回值不是引用会调用拷贝构造函数
                   cout << "Copyassignment operator invoked!" << endl;
                   return *this;
         }
};
 
intmain(int argc, char** argv) {
         cout << "Hello WorldC++!" << endl;
         Base a;
         Base b(a); // Base b = Base(a);
         return 0;
}
 


修改之后

#include <iostream>
using namespace std;

class Base {
public:
	Base() {cout << "Constructor invoked!" << endl;}
	~Base() {cout << "Destructor invoked!" << endl;}
	Base(const Base& rhs) {
		cout << "Copy constructor invoked!" << endl;
		operator=(rhs); // *this = rhs;
	}
	Base& operator=(const Base& rhs) { // 返回引用,可以接受
		cout << "Copy assignment operator invoked!" << endl;
		return *this;
	}
};

int main(int argc, char** argv) {
	cout << "Hello World C++!" << endl;
	Base a;
	Base b(a); // Base b = Base(a);
	return 0;
}


重载赋值操作符调用拷贝构造函数:把已有对象复制并赋值给这个对象。——多一个临时对象,而且导致循环调用重载赋值操作符。

 

例子2:重载赋值操作符调用拷贝构造函数(导致循环调用重载赋值操作符)

#include<iostream>
usingnamespace std;
 
classBase {
public:
         Base() {cout << "Constructorinvoked!" << endl;}
         ~Base() {cout << "Destructorinvoked!" << endl;}
         Base(const Base& rhs) {
                   cout << "Copyconstructor invoked!" << endl;
         }
         Base& operator=(const Base&rhs) {
                   cout << "Copyassignment operator invoked!" << endl;
                   *this = Base(rhs);
                   return *this;
         }
};
 
intmain(int argc, char** argv) {
         cout << "Hello WorldC++!" << endl;
         Base a;
         Base b(a); // Base b = Base(a);
         b = a;
         return 0;
}


总之:

Don't tryto implement one of the copying functions in terms of the other. Instead, putcommon functionality in a third function that both call.

——EffectiveC++ Third Edition By Scott Meyers Item 12: Copy all parts of an object  Things to Remember

可行的做法是专门写一个做赋值操作的成员函数,所有的copying functions都去调用这个函数实现赋值操作,免去了管理两份代码的库。

在继承中使用重载赋值operator=

先看一段代码。

class A1
{
public:
        int operator=(int a)
        {
                return 8;
        }
 
        int operator+(int a)
        {
                return 9;
        }
};
 
class B1: public A1
{
public:
        int operator-(int a)
        {
                return 7;
        }
};
 
intmain()
{       
        B1 v;
        cout << (v + 2) << endl; //OK, print 9
        cout << (v - 2) << endl; //OK, print 7
        cout << (v = 2) << endl; //Error, see below
 
        return 0;
}


首先得出基类中的 operator=不可以被派生类继承。原因如下

每一个类对象实例在创建的时候,如果用户没有定义“赋值运算符重载函数”,那么,编译器会自动生成一个隐含和默认的“赋值运算符重载函数”。所以,B1的实际上的声明应该类似于下面这种情况:

classA1 
{ 
public: 
        int operator=(int a) 
        { 
                return 8; 
        } 
 
        int operator+(int a) 
        { 
                return 9; 
        } 
}; 
 
class B1: public A1 
{ 
public: 
        B1& operator =(const B1& robj);// 注意这一行是编译器添加的 
        int operator-(int a) 
        { 
                return 7; 
        } 
}; 


C++标准规定:如果派生类中声明的成员与基类的成员同名,那么,基类的成员会被覆盖,哪怕基类的成员与派生类的成员的数据类型和参数个数都完全不同。显然,B1中的赋值运算符函数名operator =和基类A1中的operator =同名,所以,A1中的赋值运算符函数int operator=(int a);被B1中的隐含的赋值运算符函数B1& operator =(const B1& robj);所覆盖。 A1中的intoperator=(int a);函数无法被B1对象访问。

程序中语句v = 2实际上相当于v.operator=(2);,但是A1中的int operator=(int a);已经被覆盖,无法访问。而B1中默认的B1& operator =(const B1& robj);函数又与参数2的整数类型不相符,无法调用。

所以,“赋值运算符重载函数”不是不能被派生类继承,而是被派生类的默认“赋值运算符重载函数”给覆盖了。

(以上摘自http://www.cqumzh.cn/bbs/viewthread.php?tid=134570

 

那如何在派生类中的赋值操作符中调用基类的赋值操作符呢?

答案是必须显示的声明定义赋值操作符,然后在函数中显示地调用基类的赋值操作符。

D::operator=(const D&d)
{
         B::operator=(d);//显示调用
         //派生类的其他赋值;
}


同理,这种做法在拷贝构造函数中也适用。

class A
{
private:
         int x;
public:
         A(int a)
         {
                   x=a;
         }
         A(A &m);
 
};
A::A(A&m)
{
         x=m.x;
}
classB:public A
{
private:
         int y;
public:
         B(int a,int b):A(a),y(b){}
         B(B &k):A(k);
 
         void print()
         {
                   cout<<"y="<<y<<endl;
         }
};
B::B(B&k):A(k)
{
         y=k.y;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值