文章部分内容参考知乎用户 黄昏 的一篇文章。感谢♥ ♥
该文章地址:https://zhuanlan.zhihu.com/p/...
左值引用的定义
我们在程序中常用的引用为左值引用(右值引用是C++11新增的特性,在此不进行讨论)。首先来看左值引用:
int a = 10;
int &b = a; // 定义一个左值引用向量
b = 100; // 通过左值引用来修改引用内存的值
引用的本质为一个指针常量,那么int &b = a
其实为:int* const b = &a
。明白了这一点,我们就能理解以下代码无法编译通过的原因。
int &b = 10;
这是因为10无法进行取地址操作,也就是无法对立即数取地址(这是因为立即数存储在寄存器中,而不是内存里)。但是,我们就想引用常量数字10该怎么办呢?我们可以使用常引用:
const int &b = 10;
上面这句代码是可以编译通过的。因为当我们使用常引用来引用常量数字10时,此时内存上产生了临时变量,而临时变量是可以取地址的。相当于:
const int temp = 10; // temp为临时变量
const int &b = temp;
总结一下:
左值引用要求右边的值必须取得地址。如果无法取得地址,就需要使用常引用。但使用常引用后,只能通过引用读取数据,而无法修改数据。
加号运算符重载
定义一个student类,student.h文件如下:
#pragma once
#include
using namespace std;
class student
{
friend ostream& operator<<(ostream& cout, const student& stu); /*
*左移运算符重载,请注意这里的第二个形
*参,为何加了const?仅仅因为防止对象内容 *被修改?
*/
public:
student(int, int);
student(const student&); // 同样的,自定义的拷贝构造函数为何也加了const?
~student();
student operator+(const student& stu); /*
*文章标题为重载+号运算符和引用的关系。因此,下面将首先讲解这里加
*const的原因,随后引出上面两个疑问的答案。
*/
private:
int* mPWeight;
int* mPHeight;
};
student.cpp文件:
#include "student.h"
student::student(int weight, int height)
{
mPWeight = new int(weight);
mPHeight = new int(height);
}
student::student( const student& stu)
{
mPWeight = new int(*stu.mPWeight);
mPHeight = new int(*stu.mPHeight);
}
student student::operator+(const student& stu)
{
student temp(*mPWeight + *stu.mPWeight, *mPHeight + *stu.mPHeight);
return temp;
}
student::~student()
{
if (mPWeight != NULL)
{
delete mPWeight;
mPWeight = NULL;
}
if (mPHeight != NULL)
{
delete mPHeight;
mPHeight = NULL;
}
}
main.cpp文件:
#include"student.h"
ostream& operator<<(ostream& cout, const student& stu)
{
cout << *(stu.mPWeight) << " " << *(stu.mPHeight);
return cout;
}
int main()
{
student student1(120, 180);
student student2(110,170);
student student3 = student1 + student2;
cout << student1 + student2;
return 0;
}
在解答student.h中的两个疑问之前,我们还要了解左值和右值的含义。在C++中左值和右值没有标准的定义,但以下说法是得到普遍认可的:
可取地址的,有名字的,非临时的为左值;
不可取地址的,没名字的,临时的为右值。
那么,立即数、函数返回的值、匿名对象为右值(这点太重要了,是本篇文章的精髓),非匿名对象(包含变量)、函数返回的引用。
再来看student.cpp中的+号重载函数,返回了一个值!!!那么再来看main.cpp中的 student student3 = student1 + student2;
,这行代码的运行过程为:student1 + student2返回一个匿名对象(为方便起见,我们用temp代表该匿名对象);再调用student类的拷贝构造函数,此时:const student& stu = temp。看到这里,应该明白了叭。假如为:student& stu = temp;
,temp是右值,无法取地址,也就无法使用左值引用。需要加const来构成常量引用(移步第一部分:左值引用的定义)。这样才可以编译通过。所以拷贝构造函数的形参加const的原因远远不止于:防止传入的对象内容被更改。 所以我们在写拷贝构造函数时,必须要加const。
<<重载函数的疑问也就随之解开了。