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

九.C++ 对引用的学习

一.基本概念

引用即内存的别名

int a = 10;

int& b = a;

引用本身不占用内存,并非实体,对引用的所有操作都是在对目标内存进行操作

引用必须初始化,且不能更换对象

int c = 5;

b = c; // × 仅仅是在对引用的目标内存进行赋值

#include <iostream>
using namespace std;int main() {int mm1 = 20;  // 1. 定义整型变量mm1并初始化为20int& mm2 = mm1; // 2. 定义整型引用mm2,绑定到mm1int mm3 = 15;  // 3. 定义整型变量mm3并初始化为15mm2 = mm3;  // 4. 将mm3的值赋给mm2(即mm1)cout << "mm1: " << mm1 << endl;  // 输出 15cout << "mm2: " << mm2 << endl;  // 输出 15cout << "mm3: " << mm3 << endl;  // 输出 15return 0;
}

详细执行过程:

  1. int mm1 = 20;

    • 定义一个整型变量 mm1,并将其初始值设为 20

    • 内存中:mm1 存储的值是 20

  2. int& mm2 = mm1;

    • 定义一个整型引用 mm2,并将其绑定到 mm1

    • 引用 mm2 并不是一个新的变量,而是 mm1 的别名(另一个名字)。

    • 此时,mm2mm1 指向同一块内存,修改 mm2 就是修改 mm1,反之亦然。

  3. int mm3 = 15;

    • 定义一个整型变量 mm3,并将其初始值设为 15

    • 内存中:mm3 存储的值是 15

  4. mm2 = mm3;

    • mm3 的值(15)赋给 mm2

    • 由于 mm2mm1 的引用,这实际上是将 15 赋给 mm1

    • 执行后:

      • mm1 的值变为 15

      • mm2 的值也变为 15(因为它是 mm1 的引用)。

      • mm3 的值仍然是 15(不受影响)。

最终结果:

  • mm1 的值:15(被 mm2 = mm3 修改)

  • mm2 的值:15(因为它是 mm1 的引用)

  • mm3 的值:15(保持不变)

输出

mm1: 15
mm2: 15
mm3: 15

关键点总结:

  1. 引用(&)的本质

    • 引用是变量的别名,不是独立的变量。

    • int& mm2 = mm1; 表示 mm2mm1 的另一个名字,两者共享同一块内存。

  2. 赋值操作的影响

    • mm2 = mm3; 实际上是将 mm3 的值赋给 mm1(因为 mm2mm1 的引用)。

    • 引用本身没有独立的内存空间,赋值操作会直接影响它绑定的变量。

  3. 与指针的区别

    • 如果是 int* p = &mm1; *p = mm3;,效果和引用类似,但引用更简洁且不能重新绑定。

    • 引用一旦绑定后不能更改绑定的对象,而指针可以指向不同的对象。

引用本身一旦绑定后不能重新绑定到另一个对象,引用不能改变

关于 “引用不能改变” 的说法需要更精确的解释。实际上,引用本身一旦绑定后不能重新绑定到另一个对象,但 引用绑定的对象的内容是可以改变的。这是两个不同的概念:

1. 引用的“不可变性”(不能重新绑定)

  • 引用一旦初始化后,不能指向其他变量

    • 引用在声明时必须初始化,并且之后不能更改它绑定的对象。

    • 例如:

int a = 10;
int b = 20;
int& ref = a;  // ref 绑定到 a
ref = b;       // 这是修改 ref 绑定的对象 a 的值,不是改变 ref 的绑定!// ref = &b;   // 错误!不能重新绑定 ref 到 b(C++ 不允许)
  • 关键点ref = b 是把 b 的值赋给 a(因为 refa 的引用),而不是让 ref 指向 b

2. 引用绑定的对象的内容可以改变

引用本身是别名,修改引用就是修改它绑定的对象

  • 例如:

int a = 10;
int& ref = a;  // ref 是 a 的别名
ref = 20;      // 修改 ref 就是修改 a,a 现在是 20

常见误区澄清

  • 误区:“引用不能改变” → 实际上是指 引用不能重新绑定,但可以修改它绑定的对象的内容。

    正确说法:

    • 引用 不能重新绑定(不能指向其他对象)。

    • 引用 可以修改它绑定的对象的内容(因为它是别名)。

总结

  • 引用一旦绑定后不能重新绑定(不能指向其他变量),这是它的“不可变性”。

  • 引用绑定的对象的内容可以改变(通过引用直接修改)。

 mm2 = mm3
  • mm2mm1 的引用,所以 mm2 = mm3 就是 mm1 = mm3

  • 修改的是 mm1 的值,mm2 仍然绑定到 mm1(不能重新绑定)。

二.引用的常属性必须和目标的常属性“一致”(个别情况也可以不一致)

const int e = 10;

int& f = e; // ×

const int& g = e; // √

可以限定更加严格(别名可以比真名更加严格)

int a = 10;const int& h = a;   // OK

三.const 修饰一个常量(常引用)

在 C++ 中,const 引用const T&)是一种非常重要的特性,它结合了引用的效率和 const 的安全性。

1. 基本概念
  • const 引用

    • 是一个对常量对象的引用,不能通过该引用修改所绑定的对象。

    • 语法:const T& ref = obj;

    • 作用:提供对对象的只读访问,同时避免拷贝(与普通引用类似)。

    与普通引用的区别

特性普通引用 (T&)const 引用 (const T&)
能否修改对象可以修改绑定的对象不能修改绑定的对象
绑定对象类型必须绑定可修改的对象可以绑定 const 或非 const 对象
安全性可能意外修改数据提供只读访问,更安全
2. 核心知识点
(1) const 引用可以绑定到 const 和非 const 对象
  • 可以绑定到非 const 对象(提供只读访问):

int x = 10;
const int& ref = x;  // ref 是 x 的 const 引用
// ref = 20;         // 错误!不能通过 ref 修改 x
  • 可以绑定到 const 对象(合法且安全):

const int y = 20;
const int& ref = y;  // ref 是 y 的 const 引用
// ref = 30;         // 错误!y 本身就是 const
  • 普通引用不能绑定到 const 对象

const int z = 30;
int& ref = z;        // 错误!普通引用不能绑定 const 对象
(2) const 引用可以延长临时对象的生命周期
  • 临时对象(如函数返回值、表达式结果)通常只能绑定到 const 引用

  • 用途:避免不必要的拷贝,同时保证安全性(不能修改临时对象)

10;   // 声明周期很短,正常执行过了封号后 10这个内存地址就不在了int& ri = 10;  // 报错      非常引用的初始值必须为左值,普通引用不能绑定临时对象const int& ri = 10;  // 正确,有了这个别名之后,声明周期就变长了,会跟着别名的生命周期改变

了解一个左值和右值的概念:

具体名字的内存,能够取地址 --》 左值 (非常左值[无const修复] | 常左值[有const修复])

匿名内存,不能取地址值 --》 右值 (认为直接更改右值没有意义) 比如 10;

常引用可以作为任何东西的别名

特点:

1.引用可以延长右值的生命周期

2.常引用 即 万能引用

3.引用的生命周期不能长于目标

int a = 10;int& ra = a;	// OK  引用ra可以是a的别名
const int& ri = a;  // OK  常引用 ri 可以是 a的别名const int b = 20;int& bb = b;   // ERROR  const int& bb = b;   // OK  常引用bb可以是常量b的别名const int& rf = 10;  // OK  常引用rf可以是常量10的别名int foo(){}const int& hg = foo();   // OK
(3) const 引用作为函数参数(推荐用法)
  • 传递大对象时避免拷贝,同时防止函数内部修改参数:

void printValue(const std::string& str) {// str = "new value";  // 错误!不能修改std::cout << str << std::endl;
}int main() {std::string s = "Hello";printValue(s);  // 传递 const 引用,避免拷贝
}

 

优势:

  • 高效(无拷贝)。

  • 安全(函数不能意外修改参数)。

(4) const 引用与返回值
  • 函数返回 const 引用:

    • 可以避免返回临时对象的拷贝,同时防止调用者修改返回值:

const std::string& getString() {static std::string s = "Hello";return s;  // 返回 static 变量的 const 引用
}

注意:如果返回局部变量的引用,会导致悬空引用(未定义行为)!  

const std::string& getLocalString() {std::string s = "Hello";return s;  // 错误!s 是局部变量,函数结束后被销毁
}
(5) const 引用与重载
  • const 引用可以区分重载函数

void func(const int& x) { std::cout << "const ref" << std::endl; }
void func(int& x) { std::cout << "non-const ref" << std::endl; }int main() {int a = 10;const int b = 20;func(a);  // 调用非 const 版本func(b);  // 调用 const 版本
}

 

  • 编译器会根据参数是否为 const 选择正确的重载版本。

(6) const 引用与移动语义
  • const 引用不能用于移动语义

    • 移动语义需要修改源对象(“窃取”资源),而 const 引用禁止修改:

std::string s = "Hello";
const std::string& ref = s;
// std::string moved = std::move(ref);  // 错误!ref 是 const

 正确做法:传递非 const 引用或值:

std::string moved = std::move(s);  // 正确
3. 最佳实践
  1. 优先使用 const 引用传递参数

    • 避免拷贝,同时保证函数不会意外修改参数。

    • 示例:

void process(const std::vector<int>& data) { ... }
  1. 返回 const 引用时注意生命周期

  • 只能返回全局/静态对象的引用,不能返回局部变量的引用。

  1. 区分 const 和非 const 重载

  • 提供更灵活的接口(如 std::vectoroperator[]const 和非 const 版本)。

  1. 避免滥用 const 引用

  • 如果需要修改参数,使用普通引用。

  • 如果需要返回可修改的对象,返回值或非 const 引用。

4. 常见误区
  • 误区 1:“const 引用不能绑定到临时对象”

    • 纠正const 引用可以绑定到临时对象(普通引用不能)。

  • 误区 2:“const 引用可以修改绑定的对象”

    • 纠正const 引用不能修改绑定的对象(这是它的核心特性)。

  • 误区 3:“const 引用总是比值传递好”

    • 纠正:如果对象很小(如 int),值传递可能更高效(避免引用开销)。

5. 总结
特性const 引用 (const T&)
绑定对象可以绑定 const 或非 const 对象
能否修改对象不能(提供只读访问)
临时对象绑定可以绑定临时对象(普通引用不能)
函数参数推荐用于大对象,避免拷贝且保证安全性
返回值可以返回 const 引用(但需注意生命周期)
重载区分可以与非 const 引用重载
移动语义不能用于移动语义(因为不能修改源对象)

核心原则

  • const 引用 = 高效 + 安全(避免拷贝,防止意外修改)。

  • 普通引用 = 高效 + 可修改(需要修改参数时使用)。

  • 值传递 = 简单 + 安全(小对象或需要独立副本时使用)。

建议:合理使用 const 引用可以显著提升代码的性能和安全性!

    http://www.xdnf.cn/news/772165.html

    相关文章:

  • 统计随机行走的结构占比
  • JDK21深度解密 Day 12:大规模迁移JDK21方法论
  • PAT-甲级JAVA题解(更新中...)
  • RGB888色彩格式转RGB565格式
  • 海外tk抓包简单暴力方式
  • 从 Windows 7 到 AnduinOS:安装、故障排除与远程控制指南
  • NLP学习路线图(十八):Word2Vec (CBOW Skip-gram)
  • 光伏功率预测 | BiLSTM多变量单步光伏功率预测(Matlab完整源码和数据)
  • 文件索引:数组、二叉树、二叉排序树、平衡树、红黑树、B树、B+树
  • 并查集(上)
  • javaFX eclipse配置
  • Redis数据类型操作命令
  • 考研系列—操作系统:(补充)第七章、输入输出系统
  • 第12次12: 修改和删除收货地址
  • 普通二叉树 —— 最近公共祖先问题解析(Leetcode 236)
  • 专业C++Qt开发服务,助力您的软件项目腾飞!
  • 二叉树的构建与逆构建/二叉查找树与替罪羊树
  • BUUCTF[HCTF 2018]WarmUp 1题解
  • 《人性的弱点》能带给我们什么?
  • C++哈希表:冲突解决与高效查找
  • uni-id-pages login-by-google实现
  • 05.MySQL表的约束
  • 使用免费wordpress成品网站模板需要注意点什么
  • NX847NX855美光固态闪存NX862NX865
  • 向量空间的练习题目
  • 前端高频面试题2:JavaScript/TypeScript
  • SOC-ESP32S3部分:26-物联网MQTT连云
  • 《深度剖析:基于Meta的GameFormer构建自博弈AI游戏代理》
  • 趋势因子均值策略思路
  • 基于STM32的循迹避障小车的Proteus仿真设计