【一篇即毕业系列】C++的引用从基础到通天
文章目录
- 一、基础概念
- 二、引用的特性
- 1. 引用是别名
- 2. 引用必须初始化
- 3. 引用不能重新绑定
- 4. 引用没有独立的内存地址
- 三、使用场景
- 1. 函数参数传递
- 2. 函数返回值
- 3. 避免大对象拷贝
- 四、传值 vs 传引用的效率对比
- 传值
- 传引用
- 效率对比示例
- 五、常引用
- 常引用的优势
- 六、权限放大、缩小、不变
- 1. 权限不变(推荐)
- 2. 权限缩小(允许)
- 3. 权限放大(不允许)
- 函数参数中的权限规则
- 七、引用 vs 指针
- 相似点
- 主要区别
- 代码对比
- 使用建议
- 总结
首先列出一下csdn 引用的经典文章:
csdn引用理解
这篇文章讲的非常好。
然后我们开始本文讲解:
我来为你详细讲解C++的引用,从基础概念到高级特性。
一、基础概念
**引用(Reference)**是C++中的一种复合类型,它是某个已存在变量的别名。引用不占用额外的存储空间,它只是目标变量的另一个名字。
inta=10;int&ref=a;// ref是a的引用,即a的别名关键点:
- 引用必须在声明时初始化
- 引用一旦初始化,就不能重新绑定到其他变量
- 引用必须引用有效的对象,不能是NULL
二、引用的特性
1. 引用是别名
intx=5;int&ref=x;ref=10;// 修改ref就是修改x,x现在也是102. 引用必须初始化
int&ref;// 错误!引用必须初始化inta=5;int&ref=a;// 正确3. 引用不能重新绑定
inta=5,b=10;int&ref=a;ref=b;// 这不是重新绑定,而是将b的值赋给a4. 引用没有独立的内存地址
inta=5;int&ref=a;cout<<&a<<endl;// 输出相同的地址cout<<&ref<<endl;// 输出相同的地址三、使用场景
1. 函数参数传递
voidswap(int&a,int&b){inttemp=a;a=b;b=temp;}intmain(){intx=5,y=10;swap(x,y);// x和y的值会被交换}2. 函数返回值
int&getElement(intarr[],intindex){returnarr[index];// 返回引用,可以修改数组元素}intmain(){intarr[5]={1,2,3,4,5};getElement(arr,2)=100;// 修改arr[2]为100}3. 避免大对象拷贝
structBigData{intdata[1000];};voidprocess(constBigData&data){// 使用引用避免拷贝大对象}四、传值 vs 传引用的效率对比
传值
voidfunc(intx){x=x+1;}intmain(){inta=5;func(a);// a的值不变,因为传递的是副本}特点:
- 会创建参数的副本
- 对于基本类型(int, char等)效率差异不大
- 对于大对象(结构体、类对象)会有明显的性能开销
传引用
voidfunc(int&x){x=x+1;}intmain(){inta=5;func(a);// a的值会改变}特点:
- 不创建副本,直接操作原对象
- 对于基本类型效率提升不明显
- 对于大对象效率提升显著
效率对比示例
#include<iostream>#include<chrono>structLargeStruct{intdata[10000];};voidbyValue(LargeStruct s){s.data[0]=1;}voidbyReference(LargeStruct&s){s.data[0]=1;}intmain(){LargeStruct obj;autostart1=std::chrono::high_resolution_clock::now();for(inti=0;i<10000;i++){byValue(obj);}autoend1=std::chrono::high_resolution_clock::now();autostart2=std::chrono::high_resolution_clock::now();for(inti=0;i<10000;i++){byReference(obj);}autoend2=std::chrono::high_resolution_clock::now();// 传引用会明显快于传值}五、常引用
常引用用于防止通过引用修改被引用的对象:
voidprintValue(constint&ref){cout<<ref<<endl;// ref = 10; // 错误!不能通过常引用修改值}intmain(){inta=5;printValue(a);// 可以传递printValue(10);// 也可以传递临时对象}常引用的优势
- 防止意外修改
- 可以接受临时对象(右值)
- 提高代码安全性
六、权限放大、缩小、不变
1. 权限不变(推荐)
inta=10;int&ref=a;// 权限不变:可变→可变constint&cref=a;// 权限缩小:可变→不可变constintb=20;constint&cref2=b;// 权限不变:不可变→不可变2. 权限缩小(允许)
inta=10;constint&ref=a;// 允许:从可变到不可变3. 权限放大(不允许)
constinta=10;int&ref=a;// 错误!不能从不可变到可变函数参数中的权限规则
voidfunc1(int&ref){// 需要可变引用}voidfunc2(constint&ref){// 可以接受可变和不可变引用}intmain(){inta=10;constintb=20;func1(a);// 正确func1(b);// 错误!权限放大func2(a);// 正确func2(b);// 正确}七、引用 vs 指针
相似点
- 都可以用来间接访问对象
- 都可以作为函数参数传递
主要区别
| 特性 | 引用 | 指针 |
|---|---|---|
| 初始化 | 必须初始化 | 可以不初始化 |
| 重新绑定 | 不能重新绑定 | 可以重新指向其他对象 |
| 空值 | 不能为NULL | 可以为nullptr |
| 内存占用 | 通常不占用额外内存 | 占用指针大小的内存 |
| 运算符 | 使用&声明,直接使用 | 使用*声明和解引用 |
| 语法 | int& ref = a; | int* ptr = &a; |
代码对比
// 引用inta=10;int&ref=a;ref=20;// 直接使用// 指针inta=10;int*ptr=&a;*ptr=20;// 需要解引用// 指针可以重新指向intb=30;ptr=&b;// 正确// 引用不能重新绑定int&ref2=a;ref2=b;// 这是赋值,不是重新绑定使用建议
使用引用的情况:
- 参数传递(特别是大对象)
- 函数返回值
- 确定总是有效的情况
- 需要简洁语法时
使用指针的情况:
- 需要重新绑定
- 需要表示"无值"(nullptr)
- 需要指针运算
- 实现数据结构(链表、树等)
总结
引用是C++中强大的特性,它提供了:
- 高效的参数传递机制
- 简洁的语法
- 更好的代码安全性(配合const使用)
理解引用的特性、权限规则以及与指针的区别,对于编写高效、安全的C++代码至关重要。在实际开发中,优先使用引用(特别是const引用)来传递参数,可以显著提高代码的效率和可读性。
