当前位置: 首页 > news >正文

【C++】引用的本质与高效应用

目录

  • 🚀前言
  • 🌟引用的基础语法与注意事项
    • 💯基础语法
    • 💯引用与指针的本质区别
    • 💯引用的注意事项
  • ☘️引用的核心应用场景
    • 💯引用做函数参数
      • 🎯先看值传递的问题
      • 🎯引用传递的正确写法
    • 💯引用做函数返回值
      • 🎯错误:返回局部变量的引用
      • 🎯正确:返回静态变量的引用
    • 💯常量引用:安全且灵活
      • 🎯常量引用的3个作用
  • 🎋引用的本质
  • 💻完整可运行代码
  • 🗡️总结

🚀前言

在这里插入图片描述

大家好!我是 EnigmaCoder

  • 本文将介绍C++中引用的相关知识,包含语法、注意事项、应用场景等等。

🌟引用的基础语法与注意事项

💯基础语法

引用的核心是给已存在的变量起“别名”,操作引用和操作原变量完全等价——因为它们指向同一块内存。
语法格式数据类型 &别名 = 原变量
比如给变量a起别名b,写法是int a = 10; int &b = a;,此时修改b的值,a也会同步变化。

举个基础例子,直观感受引用的作用:

#include<iostream>
using namespace std;int main(){int a = 10;  // 原变量int &b = a;  // 给a起别名b,必须绑定原变量// 操作引用等同于操作原变量cout << "初始a:" << a << endl;  // 输出10cout << "初始b:" << b << endl;  // 输出10(和a值相同)b = 100;  // 修改别名bcout << "修改后a:" << a << endl;  // 输出100(a同步变化)cout << "修改后b:" << b << endl;  // 输出100return 0;
}

💯引用与指针的本质区别

引用本质是“指针常量”(数据类型 *const 指针),但代码层面与指针有明显区别,具体差异如下表所示:

对比维度引用指针
内存占用代码层面不体现额外内存,&引用获取的是原变量的地址本身占用内存(32位系统4字节,64位系统8字节),&指针获取的是指针自身的地址
空值情况必须绑定有效变量,不能为NULL可以指向NULL(空指针),表示无有效指向目标
指向修改一旦绑定变量,无法改绑其他变量可随时修改指向,如int *p=&a; p=&b;(从指向a改为指向b
解引用操作无需手动解引用,直接用别名操作原变量(如b=100需要用*解引用才能操作目标变量(如*p=100
初始化要求必须在定义时初始化(绑定变量),如int &b=a;可先定义后赋值,如int *p; p=&a;(定义时不初始化也不会报错)

💯引用的注意事项

使用引用时,有3个必须遵守的规则,否则会报错或导致程序异常:

  1. 必须初始化:引用不能“先定义后绑定”,比如int &b;是错误的——因为引用本质依赖原变量,没绑定变量的引用没有意义。
  2. 初始化后不能改指向:引用一旦绑定某个变量,就不能再切换绑定其他变量。比如int a=10, c=20; int &b=a;之后,不能再写int &b=c;(重复绑定错误);但可以写b=c;,这是“把c的值赋给b(即a)”,不是改绑定。
  3. 普通引用不能直接绑定字面量:字面量(如103.14)没有实际内存地址,普通引用无法直接绑定,比如int &b=10;是错误的。若要引用字面量,需用后面会讲的“常量引用”。

☘️引用的核心应用场景

💯引用做函数参数

我们写函数时,常需要通过函数修改外部变量(比如交换两个数),但普通“值传递”做不到——因为值传递会拷贝实参的副本,函数内改的是副本,不是原变量。而引用做参数能直接操作实参,还能提升效率。

🎯先看值传递的问题

普通值传递无法修改实参,代码如下:

// 错误示范:值传递修改不了实参
void SwapValue(int x, int y){int temp = x;x = y;  // 改的是形参x(实参的副本)y = temp;  // 改的是形参y(实参的副本)
}int main(){int a=20, b=30;cout << "交换前:a=" << a << ",b=" << b << endl;  // 输出20 30SwapValue(a, b);  // 传参时拷贝a、b的副本给x、ycout << "交换后:a=" << a << ",b=" << b << endl;  // 还是20 30(实参没改)return 0;
}

🎯引用传递的正确写法

用引用做形参,形参就成了实参的别名,函数内改形参就是改实参:

// 正确示范:引用传递修改实参
void SwapRef(int &x, int &y){  // x是a的别名,y是b的别名int temp = x;x = y;  // 改x = 改ay = temp;  // 改y = 改b
}int main(){int a=20, b=30;cout << "交换前:a=" << a << ",b=" << b << endl;  // 输出20 30SwapRef(a, b);  // 直接传实参,无需传地址cout << "交换后:a=" << a << ",b=" << b << endl;  // 输出30 20(实参已改)return 0;
}

引用传递的优势:比指针传递少了*解引用的操作,代码更简洁;比值传递效率高,尤其传递大型对象(如vectorstring)时,不用拷贝大量数据。

💯引用做函数返回值

引用也能作为函数返回值,但有个关键坑:不能返回局部变量的引用,只能返回静态变量或全局变量的引用。

🎯错误:返回局部变量的引用

局部变量存放在“栈区”,函数执行完后,栈区内存会被系统回收,此时返回的引用指向“已释放的内存”,读取的值是乱码或不确定的(称为“野引用”):

// 错误:返回栈区局部变量的引用
int &TestBad(){int a = 10;  // 局部变量,函数结束后释放return a;    // 返回的引用指向无效内存
}int main(){int &ref = TestBad();cout << ref << endl;  // 可能输出乱码(内存已释放)return 0;
}

🎯正确:返回静态变量的引用

静态变量存放在“全局区”,生命周期和程序一致,函数结束后不会释放,引用始终有效。甚至可以用函数调用作为“左值”直接赋值:

// 正确:返回全局区静态变量的引用
int &TestGood(){static int a = 10;  // 静态变量,全局区存储return a;
}int main(){int &ref = TestGood();TestGood() = 1000;  // 函数调用做左值,修改静态变量acout << ref << endl;  // 输出1000(引用有效)cout << TestGood() << endl;  // 输出1000(引用有效)return 0;
}

💯常量引用:安全且灵活

普通引用有两个局限:不能直接绑定字面量、可能意外修改实参。而“常量引用”(const 数据类型 &)能解决这两个问题,还能提升大型对象的传参效率。

🎯常量引用的3个作用

  1. 直接引用字面量:普通引用不能绑定10这样的字面量,但常量引用可以——编译器会自动生成临时变量,让引用绑定临时变量。比如:

    const int &ref = 10;  // 正确,编译器生成临时变量int temp=10;
    cout << ref << endl;  // 输出10
    
  2. 防止误修改实参:函数参数用常量引用后,函数内不能修改参数值,避免意外篡改实参。比如多人协作时,用常量引用传递参数,能明确“这个参数只读取,不修改”:

    // 常量引用做参数,函数内不能改a
    void Print(const int &a){// a = 100;  // 错误!const修饰后不能修改cout << a << endl;
    }int main(){int num = 20;Print(num);  // 输出20,num不会被修改return 0;
    }
    
  3. 高效传递大型对象:传递vectorstring或自定义类等大型对象时,值传递会拷贝整个对象(耗时耗内存),而常量引用只传地址,无拷贝,效率高。比如:

    #include<vector>
    // 常量引用传递vector,高效且安全
    void PrintVec(const vector<int> &vec){for(int num : vec){cout << num << " ";}
    }int main(){vector<int> vec = {1,2,3,4,5};PrintVec(vec);  // 输出1 2 3 4 5,无拷贝return 0;
    }
    

🎋引用的本质

很多人疑惑“为什么引用不能改指向”,其实引用的本质是指针常量数据类型 *const 指针名)——指针的指向不能改,但指向的值可以改。编译器会自动把引用转换成指针常量,帮我们隐藏了指针的复杂操作,让代码更简洁。

比如这段代码:

void TestRef(int &ref){ref = 100;  // 编译器自动转为:*ref = 100;
}int main(){int a = 10;int &ref = a;  // 编译器自动转为:int *const ref = &a;TestRef(ref);cout << a << endl;  // 输出100return 0;
}

可以看到,引用的“不能改指向”,本质是指针常量“指向固定”;引用的“能改值”,本质是指针常量“指向的值可改”。编译器帮我们做了“指针到引用”的转换,所以我们写代码时不用手动解引用(*),更简单。

💻完整可运行代码

下面是整合所有知识点的完整代码,可直接复制运行,帮助理解:

#include<iostream>
#include<vector>
using namespace std;// 1. 引用做函数参数:交换两数
void SwapRef(int &x, int &y){int temp = x;x = y;y = temp;
}// 2. 引用做函数返回值:正确(静态变量)
int &TestGood(){static int a = 10;return a;
}// 3. 常量引用做参数:防止修改+高效传参
void PrintVec(const vector<int> &vec){for(int num : vec){cout << num << " ";}cout << endl;
}int main(){// 基础引用使用int a = 10;int &b = a;b = 100;cout << "1. 基础引用:a=" << a << ",b=" << b << endl;// 引用做函数参数int c=20, d=30;SwapRef(c, d);cout << "2. 引用交换:c=" << c << ",d=" << d << endl;// 引用做函数返回值int &ref = TestGood();TestGood() = 1000;cout << "3. 静态变量引用:" << ref << endl;// 常量引用传递大型对象vector<int> vec = {1,2,3,4,5};cout << "4. 常量引用传vector:";PrintVec(vec);// 常量引用绑定字面量const int &ref2 = 50;cout << "5. 常量引用绑字面量:" << ref2 << endl;return 0;
}

运行结果

1. 基础引用:a=100,b=100
2. 引用交换:c=30,d=20
3. 静态变量引用:1000
4. 常量引用传vector:1 2 3 4 5 
5. 常量引用绑字面量:50

🗡️总结

引用是C++里“简洁又安全”的工具,核心价值在于:

  1. 做函数参数:直接修改实参,比指针简洁,比值传递高效;
  2. 做函数返回值:仅返回静态/全局变量的引用,避免野引用;
  3. 常量引用:安全引用字面量、防止误改实参、高效传递大型对象。

新手用引用时,记住 “三不原则”:不返回局部变量的引用、不未初始化引用、不修改常量引用的值,就能轻松避开大部分坑。


文章转载自:

http://cxeAWf8N.mLfmj.cn
http://FwSVKcha.mLfmj.cn
http://XiIY5cl0.mLfmj.cn
http://2MmtAedA.mLfmj.cn
http://R1YzXLZn.mLfmj.cn
http://LvUJDrM3.mLfmj.cn
http://EXEhD1sr.mLfmj.cn
http://B0S2brXk.mLfmj.cn
http://NgKPGeSU.mLfmj.cn
http://vFzYyVkW.mLfmj.cn
http://kfVZDH7h.mLfmj.cn
http://h5d6r9O2.mLfmj.cn
http://7HemRhwo.mLfmj.cn
http://ByAKAthk.mLfmj.cn
http://uR0Ny0vA.mLfmj.cn
http://vikytChO.mLfmj.cn
http://ZCFN4y2K.mLfmj.cn
http://GSdTuEDr.mLfmj.cn
http://CCAGTIP7.mLfmj.cn
http://ddqvCdBU.mLfmj.cn
http://CZK2IUi7.mLfmj.cn
http://TMaBRLji.mLfmj.cn
http://tqrlKaTI.mLfmj.cn
http://rD88iox0.mLfmj.cn
http://u7kmmFP2.mLfmj.cn
http://obOm9xIF.mLfmj.cn
http://aov2GZDl.mLfmj.cn
http://1ixvZpF6.mLfmj.cn
http://WzHzR1NP.mLfmj.cn
http://d7r9DJ2u.mLfmj.cn
http://www.dtcms.com/a/370005.html

相关文章:

  • Date、BigDecimal类型值转换
  • 基于Node.js和Three.js的3D模型网页预览器
  • Scikit-learn Python机器学习 - 特征降维 压缩数据 - 特征提取 - 主成分分析 (PCA)
  • CSP-J/S IS COMING
  • GraphQL API 性能优化实战:在线编程作业平台指南
  • 【基础-判断】Background状态在UIAbility实例销毁时触发,可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。
  • PageHelper的使用及底层原理
  • 探寻卓越:高级RAG技术、架构与实践深度解析
  • 【51单片机】【protues仿真】基于51单片机PM2.5空气质量检测系统
  • AI工具深度测评与选型指南 - 图像生成与编辑类
  • RabbitMQ工作模式(下)
  • Custom SRP - Complex Maps
  • tp报错解决
  • MySQL MHA 高可用集群搭建
  • 《AI大模型应知应会100篇》第68篇:移动应用中的大模型功能开发 —— 用 React Native 打造你的语音笔记摘要 App
  • Mac Intel 芯片 Docker 一键部署 Neo4j 最新版本教程
  • 正态分布 - 正态分布的经验法则(68-95-99.7 法则)
  • 【操作系统-Day 25】死锁 (Deadlock):揭秘多线程编程的“终极杀手”
  • (二).net面试(static)
  • 为什么服务器有主备BMC?
  • Dotnet 项目手动部署到AWS 和Github action CICD 流程总结
  • (2)桌面云、并行计算、分布式、网格计算
  • Java中的死锁
  • SQL 进阶指南:视图的创建与使用(视图语法 / 作用 / 权限控制)
  • SQL 实战指南:电商订单数据分析(订单 / 用户 / 商品表关联 + 统计需求)
  • 附050.Kubernetes Karmada Helm部署联邦及使用
  • 【PCIe EP 设备入门学习专栏 -- 8 PCIe EP 架构详细介绍】
  • STM32HAL 快速入门(十九):UART 编程(二)—— 中断方式实现收发及局限分析
  • 【星闪】Hi2821 | PWM脉宽调制模块 + 呼吸灯例程
  • 具身智能模拟器:解决机器人实机训练场景局限与成本问题的创新方案