⭐️ 往期相关文章
✨ 链接:C++基础知识(命名空间、输入输出、函数的缺省参数、函数重载)
⭐️ 引用
引用从语法的层面上讲:引用不是定义一个新的变量,而是给已存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
🌠 例1
#include <iostream>
using namespace std;
int main() {
int a = 10;
int& ra = a;
cout << ra << endl;
ra = 20;
cout << a << endl;
return 0;
}
ps: int& ra = a 这里的 ra 是 a 的引用(或者可以理解成 a 的别名),当 cout << ra 的时候相当于 cout << a ,当修改 ra = 20 的时候,a 的值也被修改了。
🌟 引用的特性:
- 引用在定义时必须初始化
int& ra;这种行为是error的
- 一个变量可以存在多个引用
int num = 10;int& q_num1 = num;int& q_num2 = num;
- 引用一旦引用一个实体,就不能再引用其他的实体
int num = 10;int n = 20;int q_num = num;q_num = n这里并不代表更换了引用。而是赋值:这里q_num = n是把n的值赋值给q_num而q_num又是num的引用,就相当于n的值赋值给num。
🌟 引用的使用场景:
🌠 例2
#include <iostream>
using namespace std;
// 做参数
void swap(int& qa, int& qb) {
int temp = qa;
qa = qb;
qb = temp;
}
int main() {
int a = 10;
int b = 20;
cout << a << " " << b << endl; // 10 20
swap(a , b);
cout << a << " " << b << endl; // 20 10
return 0;
}
ps: 当参数过大时,传值与传引用效率上会有一定的差距。
🌠 例3
#include <iostream>
using namespace std;
// 做返回值
int& func() {
static int count = 0;
cout << count << endl;
count++;
return count;
}
int main() {
int res = func();
res++;
func();
return 0;
}

ps: 首先这里 count 变量是被 static 修饰过的,所以并不是存在在栈区,而是静态区。return count 但是函数的返回是引用相当于返回了 count 的别名(这里可以理解为当函数栈帧销毁并且返回值的时候,会有一个临时变量来存储这个要返回的值,也就是可以理解成 int& temp = count 再把temp 引用返回),其实本质上就 count,所以当 count 的引用赋值给 res 就相当于把 count 赋值给 res 所以 res 的改变并不会影响静态区中的 count。 但是下面这种情况又不一样了。
🌠 例4
#include <iostream>
using namespace std;
int& func() {
static int count = 0;
cout << count << endl;
count++;
return count;
}
int main() {
int& res = func();
res++;
func();
return 0;
}

ps1: 这里返回值是被 int& res 接收的。而 res 相当于是静态区中 count 的别名,res++ 就相当于静态区中的 count++ , 所以第二次函数调用的时候 cout << count << endl 的时候是 2。
ps2: 如果函数中的变量会被销毁则不可以使用引用返回,则必须使用传值返回。
🌠 例5
#include <iostream>
#include <assert.h>
using namespace std;
int& At(int * nums) {
assert(nums);
return nums[0];
}
int main() {
int nums[10] = { 1 , 2 };
At(nums) = 10; // 直接可以修改返回值
cout << nums[0] << endl; // 10
return 0;
}
ps: 使用返回引用的方式是可以直接修改返回值的,但是如果使用返回值的方式是不可以直接修改的,因为返回值会创建一个临时变量来保存,这个临时变量是常值变量不可被修改。
🌟 常引用:
🌠 例6
int main () {
const int b = 20;
//int& qb = b; // error
const int& qb = b;
}
ps: b 是被 const int 修饰的常量只有读的权限 而 qb 是 int 类型的引用,这里是 error 的因为对于引用来说权限是不可以放大的。
🌠 例7
int main () {
int c = 30;
const int& qc = c; // 这样做是可以的 权限缩小
const int& d = 20; // 这样也是可以的
}
ps: 权限是可以缩小的 c 是可读可写的,而它的引用也可以是只读的。
🌠 例8
int main () {
int e = 50;
//double& dd = e; // error
const double& dd = e;
}
ps: 当把整型给到浮点型时会发生自动类型转换,但是为什么整型 e 给不了 double 的引用呢?因为在自动类型转换的时候,首先并不会改变 e 的类型,而是在它们中间创建一个临时的变量,把 e 给到这个临时变量在通过临时变量给到 dd。而这个临时变量是一个常值变量是不可以被修改的,所以这里本质上是发生权限放大的问题,如果原变量没有被 const 修饰,或者说不是一个常量,那么它的引用可以被 const 修饰也可以不被 const 修饰(引用的权限平移或者权限缩小),但是如果原变量是一个常量或者被 const 修饰,那么它的引用只能被 const 修饰,而不可以不被const修饰(权限放大)。
🌟 引用和指针的区别
在语法概念上引用就是一个别名,没有独立的空间,和引用的实体共用同一块空间。但是在底层实现上实际是有空间的,因为引用的是按照指针方式来实现的。
🌟 引用和指针的不同点:
- 引用概念上是定义一个变量的别名,而指针是存储一个变量的地址。
- 引用在定义时必须初始化,指针没有要求。
- 引用在初始化时引用一个实体后,就不能引用其他的实体,而指针可以随意的更换指向。
- 没有
NULL引用,但是有NULL指针。 - 在
sizeof中的含义不同:引用结果为引用类型的大小,但是指针始终是地址空间所占的字节个数(32位平台下4字节)。 - 引用自增
1即引用的实体增加1,指针加1代表的是指针的指向后偏移一个类型的大小。 - 有多级指针,没有多级引用。
- 访问实体的方式不同,指针需要解引用操作符,而引用编译器会自己处理。
- 引用相比指针更安全。


















