重载赋值运算符的语法

1
2
3
4
5
6
String& operator=(const String &rhs){
if(this != &rhs){ //避免自赋值
***函数体***
}
return *this;
}

特点1:返回对象的引用

特点2:参数是本类型的引用

特点3:避免自赋值

特点4:一个操作数(重载为类的成员函数,隐藏了this指针)

特点5:return *this;返回对象本身

为什么需要重载赋值运算符

在实际编程中,不光需要用一个对象去初始化一个对象,也需要将一个对象赋给另一个对象,对于基本类型而言,这种赋值是很容易完成的,但是对于复杂的自定义类的对象就不容易了。C++同样提供了默认的赋值运算符,完成的操作仍旧是简单的将源对象的数据成员的值拷贝给目标对象,这样浅拷贝操作在涉及到动态分配的时候,也会发生和复制拷贝构造相同的危险,因此,在涉及动态分配 的时候,我们就需要重载赋值运算符

重载赋值运算符和复制构造函数的区别

很简单:赋值构造函数执行前,已经有两个对象,复制构造函数执行前只有一个对象

重载赋值运算符的参数为什么是引用

我们知道,复制构造函数的参数是引用,是为了避免无限递归,那么重载赋值运算符会不会也是同样的原因呢?

1
2
3
4
5
6
7
8
9
10
test& operator=(test rhs){
...
return *this;
}
test a;
test b;
a = b;//相当于执行a.operator(b)
//由于发生的对象的值传递,会调用复制构造函数,拷贝一份临时对象rhs,传入a.operator(),只发生了一次正常拷贝

我们可以看到,只发生了一次拷贝,并不会程序崩溃,但是!!为了避免在函数调用时对实参的一次拷贝,提高效率,我们毅然决然地传递了引用(并非强制,而是推荐)。因为传引用的话,连这一次拷贝都可以被避免。

重载赋值运算符的返回值为什么是引用

现在我们来研究为什么重载赋值运算符的返回值是引用。

首先我们容易想到避免函数调用时对返回值的一次拷贝,提高效率

更重要的 ,这样可以实现 类似(a=b)=c这样的赋值。如果不是返回引用而是返回值类型,那么,执行a=b时,调用赋值运算符重载函数,在函数返回时,由于返回的是值类型,所以要对return后边的“东西”进行一次拷贝,得到一个未命名的副本(有些资料上称之为“匿名对象”),然后将这个副本返回,而这个副本是右值,所以,执行a=b后,得到的是一个右值,再执行=c就会出错。

有些人认为如果返回对象的话,就不能执行a=b=c这样的操作,其实这是错的,因为我们知道赋值运算符的结合顺序是从右往左的,先执行b=c,返回值是一个匿名对象,然后再将这个匿名对象(右值)赋给a,这样是完全可以的

类似的,我们来研究重载”<<“ ,”>>”运算符,以“<<”为例

1
2
3
4
5
6
7
8
9
ostream& operator<<(ostream& output, const test& rhs){
output<<rhs.name;
return output;
}
test a;
test b;
cout<<a<<b;

如果写成传值返回,是因为cout是左结合的,也就是先执行左边,cout<<a;可以理解为operator<<(cout,a);

所以返回值会调用拷贝构造生成一个匿名对象(右值),无法再执行后续的操作(会报错)。

另外讲了那么多引用的东西!!一定要注意!!!

1
2
3
4
5
Cset& fun(){
Cset a;
return a;
}
//大错特错!!!不能返回局部对象的引用!!!!

重载赋值运算符只能重载为类的非静态成员函数

1.之所以不是静态成员函数,是因为静态成员函数只能操作类的静态成员,不能重载非静态成员。如果我们将赋值运算符重载函数定义为静态成员函数,那么,该函数将无法操作类的非静态成员,这显然是不可行的。

2.第二点,我也搞不懂,暂且当作规定吧

赋值运算符重载函数要避免自赋值

对于赋值运算符重载函数,我们要避免自赋值情况(即自己给自己赋值)的发生,一般地,我们通过比较赋值者与被赋值者的地址是否相同来判断两者是否是同一对象

原因:

1.效率问题

2.在我们自定义重载函数时候,对于指针间的赋值,比如说p = q;我们会先delete掉p所指的空间,然后在为p重新分配空间,然后将q的内容拷贝过去。如此以来,如果发生了自赋值,那么在赋值操作前对p的delete操作,将导致p所指的数据同时被销毁。重新赋值时,拿什么来赋?

赋值运算符重载函数不可被继承

为什么赋值运算符重载函数不能被继承呢?

​ 因为相较于基类,派生类往往要添加一些自己的数据成员和成员函数,如果允许派生类继承基类的赋值运算符重载函数,那么,在派生类不提供自己的赋值运算符重载函数时,就只能调用基类的,但基类版本只能处理基类的数据成员,在这种情况下,派生类自己的数据成员怎么办?

​ 所以,C++规定,赋值运算符重载函数不能被继承。