九.C++ 对引用的学习

news2025/6/8 2:07:34

一.基本概念

引用即内存的别名

int a = 10;

int& b = a;

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

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

int c = 5;

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

#include <iostream>
using namespace std;

int main() {
    
    int mm1 = 20;  // 1. 定义整型变量mm1并初始化为20
    
    int& mm2 = mm1; // 2. 定义整型引用mm2,绑定到mm1
    
    int mm3 = 15;  // 3. 定义整型变量mm3并初始化为15
    
    mm2 = mm3;  // 4. 将mm3的值赋给mm2(即mm1)

    cout << "mm1: " << mm1 << endl;  // 输出 15
    cout << "mm2: " << mm2 << endl;  // 输出 15
    cout << "mm3: " << mm3 << endl;  // 输出 15

    return 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.coloradmin.cn/o/2403604.html

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

    相关文章

    探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面

    在开发鸿蒙应用时&#xff0c;你是否也曾为一个页面的布局反复调整&#xff1f;是否还在为查 API、写模板代码而浪费大量时间&#xff1f;今天带大家实战体验一下鸿蒙官方的 AI 编程助手——CodeGenie&#xff08;代码精灵&#xff09; &#xff0c;如何从 0 到 1 快速构建一个…

    art-pi2 上手记录(二)

    功能比较庞杂&#xff0c;写得不好&#xff0c;抛砖引玉 预备知识 stm32 默认从主闪存0x08000000启动 art-pi2的psram 映射0x90000000 art-pi2的8线ospi flash 映射0x70000000 stm32h7比较灵活&#xff0c;通过修改选项字节&#xff0c;可以实现从 0x0000 0000 到 0x3FFF 0…

    数据库SQLite基础

    SQLite的存储结构 --->B树 大型数据库 &#xff1a;Oracle 中型数据库 &#xff1a;Server是微软开发的数据库产品&#xff0c;主要支持windows平台 小型数据库 : MySQL是一个小型关系型数据库管理系统。开放源码 (嵌入式不需要存储太多数据) 一、SQLite基础 SQLite的源代码…

    1.3 古典概型和几何概型

    文章目录 古典概型模型(等可能模型)几何概型 古典概型模型(等可能模型) 两个条件&#xff1a; 1) 有限个样本点 2) 等可能性 例题&#xff1a; 设有n个人&#xff0c;每个人都等可能地被分配到N个房间中的任一间(n≤N)&#xff0c; 求下列事件的概率: (1)某指定的n间房…

    html-pre标签

    我们都知道在常见标签里面的文字的格式是不会显示的&#xff0c;比如你打了多个空格&#xff0c;但却不会显示&#xff0c;而pre标签会显示。 主要特点&#xff1a; 保留空格和换行&#xff1a;在 <pre> 标签内&#xff0c;HTML 会保留所有的空格、换行符和制表符等格式…

    【WPF】WPF 项目实战:用ObservableCollection构建一个可增删、排序的管理界面(含源码)

    &#x1f4a1;WPF 项目实战&#xff1a;构建一个可增删、排序的光源类型管理界面&#xff08;含源码&#xff09; 在实际的图像处理项目中&#xff0c;我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面&#xff0c;实现以下功能&#xff1…

    MCU_IO驱动LED

    注意事项&#xff1a; 1、亮度要求较高的情况下&#xff0c;不能由IO直接驱动LED MCU_IO引脚输出的电压和电流较弱&#xff0c;如果对光的亮度有要求的话&#xff0c;需要使用三极管来驱动。 MCU_IO的电压一般为3.3V或者5V&#xff0c;输出电流一般10mA-25mA。 2、不同颜色…

    上门预约行业技术方案全解析:小程序、App还是H5?如何选择?

    上门按摩行业这几年确实火&#xff0c;但千万别以为随便买个系统、招几个技师就能赚钱。作为一家深耕10年软件开发的公司&#xff0c;我们做了四五年上门按摩系统&#xff0c;见过太多人头脑一热冲进来&#xff0c;结果血本无归。 如果你真的想做上门按摩&#xff0c;先想清楚这…

    Java 大视界 -- 基于 Java 的大数据分布式计算在蛋白质组学数据分析中的加速与优化(255)

    &#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

    Vue.js教学第十八章:Vue 与后端交互(二):Axios 拦截器与高级应用

    Vue 与后端交互(二):Axios 拦截器与高级应用 在上一篇文章中,我们学习了 Axios 的基本用法,包括如何发送不同类型的 HTTP 请求以及基本的配置选项。本文将深入剖析 Axios 的拦截器功能,探讨请求拦截器和响应拦截器的作用、配置方法和应用场景,通过实例展示如何利用拦截…

    云计算 Linux Rocky day03(which、快捷键、mount、家目录、ls、alias、mkdir、rm、mv、cp、grep)

    云计算 Linux Rocky day03&#xff08;which、快捷键、mount、家目录、ls、alias、mkdir、rm、mv、cp、grep&#xff09; 目录 云计算 Linux Rocky day03&#xff08;which、快捷键、mount、家目录、ls、alias、mkdir、rm、mv、cp、grep&#xff09;1.which找到命令所对应的程序…

    JVM 内存溢出 详解

    内存溢出 内存溢出指的是内存中某一块区域的使用量超过了允许使用的最大值&#xff0c;从而使用内存时因空间不足而失败&#xff0c;虚拟机一般会抛出指定的错误。 在Java虚拟机中&#xff0c;只有程序计数器不会出现内存溢出的情况&#xff0c;因为每个线程的程序计数器只保…

    虚拟机CentOS 7 网络连接显示“以太网(ens33,被拔出)“、有线已拔出、CentOS7不显示网络图标

    文章目录 一、问题描述二、解决方法1、查看网络连接方式2、开启相关服务3、确认虚拟机网络连接 一、问题描述 问题描述&#xff1a;在VmWare中安装CentOS7, 启动后界面不显示网络的图标。 在GONE桌面—》设置中找到网络设置&#xff0c;发现显示线缆已拔出。 二、解决方法 …

    Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(六):图片上传交互功能

    在 《Tailwind CSS 实战&#xff1a;基于 Kooboo 构建 AI 对话框页面&#xff08;五&#xff09;》 中&#xff0c;完成了语音交互功能的优化。本文作为该系列教程的第六篇&#xff0c;将聚焦于图片上传功能的开发。通过集成图片上传与预览能力&#xff0c;我们将进一步完善 AI…

    传统的将自然语言转化为嵌入向量的核心机制是:,将离散的语言符号转化为连续的语义向量,其核心依赖“上下文决定语义”的假设和神经网络的特征提取能力。

    传统的将自然语言转化为嵌入向量的核心机制是:,将离散的语言符号转化为连续的语义向量,其核心依赖“上下文决定语义”的假设和神经网络的特征提取能力。 传统的将自然语言转化为嵌入向量(Word Embedding)的核心机制是分布式语义假设(Distributional Semantics Hypothesis…

    玄机-日志分析-IIS日志分析

    1.phpstudy-2018站点日志.(.log文件)所在路径&#xff0c;提供绝对路径 2.系统web日志中状态码为200请求的数量是多少 3.系统web日志中出现了多少种请求方法 4.存在文件上传漏洞的路径是什么(flag{/xxxxx/xxxxx/xxxxxx.xxx} 5.攻击者上传并且利用成功的webshell的文件名是什…

    【办公类-104-01】20250606通义万相50分一天用完,通义万相2.1专业版测试

    背景需求&#xff1a; 昨天打开通义万相&#xff0c;发现分数降低到3位数&#xff0c;原来时1500.仔细看&#xff0c;原来每天的50分&#xff0c;只有1天有效期了。 用掉试试&#xff0c;用的是之前的30天积分&#xff0c;还是今天的1天积分 纯白色背景&#xff0c;卡通简笔画…

    制作个人Github学术主页

    1.fork一个模板 从模板网站Jekyll Themes fork一个模板&#xff0c;并在repository name里填入yourname.github.io 2.生成自己的site 按顺序点击以下按钮&#xff0c;修改Branch为master /root 然后点击save &#xff0c;等待一会后刷新&#xff0c;便会生成一个新的site。 3.…

    FineReport模板认证找不到模板

    水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 文章目录 1.现象及排查过程2. 解决办法 1.现象及排查过程 FR模板认证下面找不到模板 由于是集群部署的FR&#xff0c;所以后台查看了sftp服务器&#xff0c;测试连接&#xff0c;连接成功。 但是…

    TomatoSCI数据分析实战:探索社交媒体成瘾

    今天我们尝试对一份社交媒体成瘾的调查数据进行几项简单的分析&#xff0c;看看可以得出哪些有意思的结论&#xff1f;图1A是这份数据的说明&#xff0c;因为篇幅太长只把部分数据贴出来&#xff08;图1B&#xff09;。 01 不同性别的成瘾程度会不同吗&#xff1f; 我们使用bo…