C++初学者指南第一步---12.引用

C++初学者指南第一步—12.引用

文章目录

  • C++初学者指南第一步---12.引用
    • 1. 功能(和限制)
      • 1.1 非常量引用
      • 1.2 常量引用
      • 1.3 auto引用
    • 2.用法
      • 2.1 范围for循环中的引用
      • 2.2 常量引用的函数形参
      • 2.3 非常量引用的函数形参
      • 2.4 函数参数的选择:copy / const& / & ?
      • 2.5 避免输出型参数
    • 3.绑定规则
      • 3.1 右值和左值
      • 3.2 引用绑定规则
    • 4. 陷阱
      • 4.1 永远不要返回函数内部的对象引用!
      • 4.2 小心引用向量元素!
      • 4.3 避免生命周期延长!

1. 功能(和限制)

1.1 非常量引用

int   i = 2;
int& ri = i;  // 引用 i

ri 和 i 都指向相同对象/内存位置。

cout << i  <<'\n';   // 2
cout << ri <<'\n';   // 2
i = 5;
cout << i  <<'\n';   // 5
cout << ri <<'\n';   // 5
ri = 88;
cout << i  <<'\n';   // 88
cout << ri <<'\n';   // 88

运行上面代码

  • 引用不能为“null”,即它们必须始终指向一个对象。
  • 引用必须始终指向相同的内存位置。
  • 引用类型必须与被引用对象的类型一致。
int  i  = 2;
int  k  = 3;
int& ri = i;     // 引用 i
ri = k;          // 将 k 的值赋给 i(ri 的目标)
int& r2;         //  编译错误: 引用必须初始化
double& r3 = i;  //  编译错误:类型必须相同

1.2 常量引用

= 对象的只读访问

int i = 2;
int const& cri = i;  //  i 的常量引用
  • cri 和 i 都指向相同对象/内存位置。
  • 但 const 表示 i 的值无法通过 cri 更改。
cout << i   <<'\n';   // 2
cout << cri <<'\n';   // 2
i = 5;
cout << i   <<'\n';   // 5
cout << cri <<'\n';   // 5
cri = 88;  //  编译错误: 常量!

运行上面代码

1.3 auto引用

引用类型是从赋值符的右侧推导出来的。

int i = 2;           
double d = 2.023;       
double x = i + d;       
auto & ri = i;        // ri:  int &
auto const& crx = x;  // crx: double const&

运行上面代码

2.用法

2.1 范围for循环中的引用

std::vector<std::string> v;
v.resize(10);
// 修改vector中的元素:
for (std::string & s : v) { cin >> s; }
// 只读访问 vector 中的元素:
for (std::string const& s : v) { cout << s; }
// 修改:
for (auto & s : v) { cin >> s; }
// 只读访问:
for (auto const& s : v) { cout << s; }

2.2 常量引用的函数形参

只读访问 ⇒ const&

  • 避免高开销的副本
  • 向函数的用户清楚地传达只读意图

示例:计算中位数的函数
需要从向量中读取值!

错误:通过复制⇒值传递正确:通过 const& ⇒ 没有副本
int median (vector);
auto v = get_samples(“huge.dat”);
auto m = median(v);
// 运行时间和内存开销很大!
int median (vector const&);
auto v = get_samples(“huge.dat”);
auto m = median(v);
// 不复制 ⇒ 没有开销!

示例:混合传递(按引用 + 按值)

incl_first_last ({1,2,4},{6,7,8,9}) → {1,2,4,6,9}
incl_first_last ({1,2,4},{6,7,8,9}) → {1,2,4,6,9}

该实现是在第一个向量的本地副本 ‘x’ 上进行操作,并且通过常量引用 ‘y’ 从第二个向量中读取:

auto incl_first_last (std::vector<int> x, std::vector<int> const& y) {if (y.empty() return x;// append to local copy 'x'x.push_back(y.front());x.push_back(y.back());return x;
}

2.3 非常量引用的函数形参

示例:交换两个变量值的函数

void swap (int& i, int& j) {int temp = i;  // copy i's value to tempi = j;         // copy j's value to ij = temp;      // copy temp's (i's original value) to j
}
int main () {int a = 5;int b = 3;swap(a,b);cout << a << '\n'   // 3<< b << '\n';  // 5
}

运行上面代码
注意:可以使用 std::swap 来交换对象的值(#include )。它可以像上面的功能一样使用,但是可以避免对于支持移动语义的对象(如std::vector)的大开销的临时复制(它的实现将在移动语义章节中解释)。
虽然在某些情况下非 const 引用可能很有用,但总体上你还是应该避免使用这种输出参数(查看下面的内容获取更多细节)。

2.4 函数参数的选择:copy / const& / & ?

void read_from (int);  // 基本类型按值传递即可
void read_from (std::vector<int> const&);
void copy_sink (std::vector<int>);
void write_to  (std::vector<int> &);

从可以廉价复制的对象读取(所有基本类型)⇒ 值传递
如:

double sqrt (double x) { … }

从内存占用量较大(> 64位)的对象中读取内存时 ⇒ 用const &传递
如:

void print (std::vector<std::string> const& v) {for (auto const& s : v) { cout << s << ' '; }
}

在函数内部需要复制的内容 ⇒ 值传递
按值传递而不是在函数内显式复制。 其原因将在更高级的文章中解释。
如:

auto without_umlauts (std::string s) {s.replace('ö', "oe");  // modify local copy…return s;  // return by value!
}

写入到函数外部对象 ⇒ 由非常量&传递
(尽管它们在某些情况下可能很有用,但总的来说,你应该避免使用这种输出参数,请参阅下面内容。)
如:

void swap (int& x, int& y) { … }

2.5 避免输出型参数

像这样有非const引用参数的函数:

void foo (int, std::vector<int>&, double);

可能会在调用位置造成混乱/歧义:

foo(i, v, j);
  • 哪个参数 (i, v, j) 改变了,哪个保持不变?
  • 引用的对象是如何以及何时更改的,它是否被更改了?
  • 引用参数只充当输出(函数只向它写入数据)还是同时充当输入(函数也从它读取数据)?
    ⇒ 一般来说很难调试和推理!
    示例:一个只会造成混乱的接口
void bad_minimum (int x, int& y) {if (x < y) y = x;
}
int a = 2;
int b = 3;
bad_minimum(a,b);  
// 哪个变量再次保存了较小的值?

3.绑定规则

3.1 右值和左值

左值 = 我们可以获取内存地址的表达式

  • 指向持久存在内存中的对象
  • 一切有名称的东西(变量、函数参数……)

右值 = 我们无法获取内存地址的表达式

  • 字面值(123,“string literal”,…)
  • 临时的运行结果
  • 从函数返回的临时对象
int a = 1;      // a 和b 都是左值
int b = 2;      // 1 和 2 都是右值
a = b;
b = a;
a = a * b;      // (a * b)表达式的结果是右值
int c = a * b;  // OK,右值可以赋值给左值
a * b = 3;      //  编译错误:不能赋值给右值
std::vector<int> read_samples(int n) { … }
auto v = read_samples(1000);

3.2 引用绑定规则

&只能绑定到左值
const&可以绑定到const左值和右值

在这里插入图片描述

4. 陷阱

4.1 永远不要返回函数内部的对象引用!

在这里插入图片描述
只有在被引用对象的生命周期长于函数的情况下才有效!
在这里插入图片描述

4.2 小心引用向量元素!

警告:std::vector 中的元素引用可能在改变向量元素数量的任何操作之后失效!

vector<int> v {0,1,2,3};
int& i = v[2];
v.resize(20);  
i = 5; //  未定义行为:原始内存可能已经释放。

悬空引用 = 指的是指向一个不再有效的内存位置的引用。
std::vector 存储元素的内部内存缓冲区在某些向量操作期间可以被替换为新的缓冲区,因此对旧缓冲区的任何引用可能会变得悬空。

4.3 避免生命周期延长!

引用可以延长临时对象(右值)的生命周期。

auto const& r = vector<int>{1,2,3,4};
⇒ 向量对象的生命周期被引用 r 延长了

从函数返回的对象呢?

std::vector<std::string> foo () { … }
以值传递(推荐)
vector<string> v1 = foo();  
auto v2 = foo();
不推荐:忽略它→立即销毁
foo()
不推荐:获取对它的常量引用 ⇒ 临时对象的生命周期被延长
...只要这个引用还存在
vector<string> const& v3 = foo();  
auto const& v4 = foo();
禁止:不要引用它的成员
返回对象的成员(这里指向量的内容)不能延长生命周期!
string const& s = foo()[0];  // 悬空引用!
cout << s;                   //  未定义行为

不要通过引用来延长生命周期!

  • 容易造成混淆
  • 容易写出错误
  • 没有真正的好处

只需按值返回对象。 这对于现代C ++ 中的大多数函数和类型来说并不涉及大开销的复制,尤其是在C++17及更高版本中。

附上原文链接

如果文章对您有用,请随手点个赞,谢谢!^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/32078.shtml

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

相关文章

git拉取gitee项目到本地

git安装等不做赘述。 根据需要选择不同操作 1.只是单纯拉取个项目&#xff0c;没有后续的追踪等操作 不需要使用git init初始化本地文件夹 新建一个文件夹用于存储项目&#xff0c;右键选择 git bash here 会出现命令行窗口 如果像我一样&#xff0c;只是拉取个项目作业&…

若以框架学习(3),echarts结合后端数据展示,暂时完结。

前三天&#xff0c;参加毕业典礼&#xff0c;领毕业证&#xff0c;顿时感到空落落的失去感&#xff0c;没有工作&#xff0c;啥也没有&#xff0c;总感觉一辈子白活了。晚上ktv了一晚上&#xff0c;由于我不咋个唱歌&#xff0c;没心情&#xff0c;听哥几个唱了一晚上周杰伦&am…

Redis实战—Redis分布式锁

本博客为个人学习笔记&#xff0c;学习网站与详细见&#xff1a;黑马程序员Redis入门到实战 P56 - P63 目录 分布式锁介绍 基于Redis的分布式锁 Redis锁代码实现 修改业务代码 分布式锁误删问题 分布式锁原子性问题 Lua脚本 编写脚本 代码优化 总结 分布式锁介绍…

express+vue在线im实现【三】

往期内容 expressvue在线im实现【一】 expressvue在线im实现【二】 本期示例 本期总结 支持各种类型的文件上传&#xff0c;常见文件类型图片&#xff0c;音频&#xff0c;视频等&#xff0c;上传时同步获取音频与视频的时长&#xff0c;以及使用上传文件的缓存路径来作为vi…

WDF驱动开发-DMA(一)

在 Windows 7 及更早版本上&#xff0c;Kernel-Mode Driver Framework (KMDF) 仅支持 (DMA) 设备的总线-主直接内存访问。 此类设备包含其自己的 DMA 控制器。 在片上系统 (SoC) 上运行Windows 8及更高版本的平台上&#xff0c;该框架还支持系统模式 DMA&#xff0c;其中多个设…

视频讲解|基于模型预测算法的含储能微网双层能量管理模型【mpc】

1 主要内容 该讲解视频对应的免费程序链接为【防骗贴】基于模型预测算法的含储能微网双层能量管理模型&#xff0c;主要做的是一个微网双层优化调度模型&#xff0c;微网聚合单元包括风电、光伏、储能以及超级电容器&#xff0c;在微网的运行成本层面考虑了电池的退化成本&…

快捷方式(lnk)--加载HTA-CS上线

免责声明:本文仅做技术交流与学习... 目录 CS: HTA文档 文件托管 借助mshta.exe突破 本地生成lnk快捷方式: 非系统图标路径不同问题: 关于lnk的上线问题: CS: HTA文档 配置监听器 有效载荷---->HTA文档--->选择监听器--->选择powershell模式----> 默认生成一…

政务大厅引导系统:AR、VR技术革新引领政务服务体验升级

一、传统政务大厅面临的普遍痛点 随着城市的发展和政务服务需求的增长&#xff0c;传统的政务大厅面临着诸多挑战和痛点&#xff1a; 信息不对称&#xff1a;政务大厅内各部门信息分散&#xff0c;群众难以快速获取全面准确的服务信息&#xff0c;导致办事效率低下。 办事流…

计算机视觉 | 基于图像处理和边缘检测算法的黄豆计数实验

目录 一、实验原理二、实验步骤1. 图像读取与预处理2. 边缘检测3. 轮廓检测4. 标记轮廓序号 三、实验结果 Hi&#xff0c;大家好&#xff0c;我是半亩花海。 本实验旨在利用 Python 和 OpenCV 库&#xff0c;通过图像处理和边缘检测算法实现黄豆图像的自动识别和计数&#xff0…

JetBrains GoLand 2024 mac/win版:高效开发,Go无止境

JetBrains GoLand 2024是一款专为Go语言开发者设计的集成开发环境(IDE)&#xff0c;为开发者带来了更加高效、智能和便捷的编程体验。 GoLand 2024 mac/win版获取 在代码编辑方面&#xff0c;GoLand 2024提供了全行代码补全功能&#xff0c;通过利用先进的深度学习模型&#x…

力扣85.最大矩形

力扣85.最大矩形 遍历所有行作为底边 做求矩形面积&#xff08;84. class Solution {public:int maximalRectangle(vector<vector<char>>& matrix) {if (matrix.empty()) return 0;int n matrix.size(),m matrix[0].size();int res0;vector<int> li…

适耳贴合的气传导耳机,带来智能生活体验,塞那Z50耳夹耳机上手

现在大家几乎每天都会用到各种AI产品&#xff0c;蓝牙耳机也是我们必不可少的装备&#xff0c;最近我发现一款很好用的分体式气传导蓝牙耳机&#xff0c;它还带有一个具备AI功能的APP端&#xff0c;大大方便了我们日常的使用。这款sanag塞那Z50耳夹耳机我用过一段时间以后&…

开发指南033-数据库兼容

元芳&#xff0c;你怎么看&#xff1f; 单一数据库自身就有一些不同处理之处&#xff0c;如果一个平台要兼容所有数据库&#xff0c;就是难上加难&#xff0c;像isnull函数各数据库就不同。 对于这类问题&#xff0c;平台采用统一自定义函数解决&#xff0c;例如上面的round函…

模式分解的概念(下)-无损连接分解的与保持函数依赖分解的定义和判断、损失分解

一、无损连接分解 1、定义 2、检验一个分解是否是无损连接分解的算法 输入与输出 输入&#xff1a; 关系模式R&#xff08;U&#xff0c;F&#xff09;&#xff0c;F是最小函数依赖集 R上的一个分解 输出&#xff1a; 判断分解是否为无损连接分解 &#xff08;1&#x…

JAVA同城服务场馆门店预约系统支持H5小程序APP源码

&#x1f4f1;一键预约&#xff0c;畅享无忧体验&#x1f3e2; &#x1f680;一、开启预约新纪元 在繁忙的都市生活中&#xff0c;我们常常因为时间紧张而错过心仪的门店或场馆服务。然而&#xff0c;有了“门店场馆预约小程序”&#xff0c;这些问题都将迎刃而解。这款小程序…

群辉NAS中文件下载的三种方案

目录 一、迅雷套件 1、添加套件来源 2、安装套件 3、手机安装迅雷 二、qBittorrent套件 1、添加套件来源 2、改手工安装 3、更新后的问题 4、最后放弃DSM6 (1)上传文件手工安装 (2)添加套件来源 5、解决登陆报错 6、添加tracker 7、修改下载默认位置 8、手机…

c++之说_15|成员函数的const尾缀修饰 ( const const)

我记得我刚接触c的时候 遇到成员函数 右边尾部 写了个const 我当时就很蒙 不过慢慢的也从大佬口中获得一二经验了 class kj{public:void get(){printf("无修饰\n");}void get()const{printf("const 修饰\n");}}; 大概就是这个样子 当时我抓耳挠腮的看…

【yolov8语义分割】跑通:下载yolov8+预测图片+预测视频

1、下载yolov8到autodl上 git clone https://github.com/ultralytics/ultralytics 下载到Yolov8文件夹下面 另外&#xff1a;现在yolov8支持像包一样导入&#xff0c;pip install就可以 2、yolov8 语义分割文档 看官方文档&#xff1a;主页 -Ultralytics YOLO 文档 还能切…

图扑助力铝型材挤压:数字孪生引领智慧管理

通过图扑数字孪生技术&#xff0c;为铝型材挤压车间提供实时监控和优化管理方案。高精度三维建模和数据可视化提升了生产效率和管理透明度&#xff0c;推动智能制造和资源优化配置。

leetcode 二分查找·系统掌握 寻找旋转排序数组中的最小值II

题目&#xff1a; 题解&#xff1a; 本题比普通的寻找旋转排序数组中的最小值多了一个数组中的元素可以重复这一点。 这会时原来的思路出现一个漏洞&#xff08;大家感兴趣可以看看我做普通版寻找旋转排序数组最小值的思路&#xff09;&#xff0c;就是旋转后的数组中的第二个…