一、引用符号
C++中,除了&作为取地址运算符可以取得某一常量或者变量的地址,在定义语句中,被称为引用符。使用方法是加在数据类型与变量名称的中间。
二、引用符号的使用
2.1 替换符
使用引用符号可以用于替换某一变量的名称,如下:
int x = 666;
int &y = x;
y = 666; //此处对y赋值相当于对x赋值
cout << "x : " << x << endl; //x为666
使用引用符号作为替换符,需要遵守以下注意事项:
1.必须在定义语句中初始化,也就是赋初值,指明它是谁的替换符。
2.在之后的赋值语句中,是没办法再改变替换对象的。下面举个例子进行说明:
int x = 666;
int &y = x; //这里初始化后,此后的代码中,y一直作为x的替换符
int z = 555;
y = z;// 这里的意思是:将z的值555赋值给y,也就是赋值给x,x = 555
cout << "x : " << x << endl; //x为555
cout << "y : " << y << endl; //y为555
cout << "z : " << z << endl; //z为555
2.2 函数返回值
引用符号作为函数返回值时,使用形式如下:
int& func(int x) {
...
}
这里请注意,2.1中我们了解过,可以将引用符号理解为一个替换符,但如果替换的那个局部变量已经结束了它的生命周期,也就是被释放了,那么我们对替换符的操作也将不受控制。我们举一个便于理解的小例子,如下:
int& func(int x) {
int y = 600; //这里y是一个func()函数的局部变量,当函数执行结束,y的空间被释放
y += x;
return y;
}
int main() {
int &a = func(66); //这里a的替换符,本质上是替换func()函数的局部变量y
(这里如果我们对a在进行操作,就可能产生未知的错误)
return 0;
}
那我们应该如何避免这种问题呢?本质山就是让局部变量y不释放,也就是让它的生命周期长一些,可以将其变为静态变量。将上述代码变成如下形式:
int& func(int x) {
static int y = 600; //这里的y变成了一个静态变量,存放在全局区
y += x;
return y;
}
int main() {
int &a = func(66); //这里a的替换符,本质上是替换func()函数的局部变量y
(这里如果我们对a在进行操作,由于y为静态变量,空间并未释放,因此正确)
return 0;
}
上述例子中,采用static修饰将y变为静态变量,这样在对a操作就不会产生错误了。
当函数返回值为引用类型时,函数可以作为左值(也就是等号左边的部分),如下:
int& func(int x) {
static int y = 600; //这里的y变成了一个静态变量,存放在全局区
y += x;
return y;
}
int main() {
int &a = func(66); //这里a的替换符,本质上是替换func()函数的局部变量y
func(55) = 777;//这里相当于 a = 777,也就是y = 777
// cout << "y : " << y << endl; 这句话是错误的,因为我们无法在主函数访问func()的局部变量y
cout << "a : " << a << endl;
return 0;
}
程序的输出结果为:
3.3 常量引用
常量引用通常用于保护引用形参不被修改,书写格式如下:
void func(const int &x) {
...
}
此时形参x被声明为常量引用,如果在函数体中存在对x值进行修改赋值的语句,则无法通过编译。
哪有兄弟们可能会有这样的疑问:我为什么不直接用形参啊?值传递一样不改变值啊。这里是因为,形参实际是重新在栈申请了变量空间,增大了内存开销,而引用可以理解为替换符,并没有申请多余的变量空间。
三、常量的本质
为了便于理解,我们可以认为引用是一种替换符,但它的本质实际是一个指针常量。
为了便于理解,我接下来画图为兄弟们解释:
上述例子中,我们知道,申请一个变量的本质就是将这段变量空间的首地址赋值给变量名。
而y又是指向变量名x的一个指针常量,y永远指向x,也就是我们之前说的初始化只有一次。
在接下来的使用中,编译器会识别出y是一个引用,就会将代码中的y替换为*y,也就是对x进行操作。