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;
}