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 temp
  i = j;         // copy j's value to i
  j = 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及更高版本中。

附上原文链接

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

62.WEB渗透测试-信息收集- WAF、框架组件识别(2)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;61.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;1&#xff09; 打开一个搜索引…

有趣的 Oracle JDBC 驱动包命名问题 - ojdbc6 和 ojdbc14 哪个新?!

有趣的 Oracle JDBC 驱动包命名问题 - ojdbc6 和 ojdbc14 哪个新?! 1 背景概述 最近协助一个小兄弟排查了某作业使用 sqoop 采集 oracle 数据的失败问题&#xff0c;问题现象&#xff0c;问题原因和解决方法都挺直观&#xff0c;但在此过程中发现了一个有趣的 Oracle JDBC 驱…

mechanize - 自动化与HTTP web服务器的交互操作

1、前言 随着自动化测试的普及与落地推广&#xff0c;出现了众多知名的自动化测试工具&#xff0c;如Selenium 、Robot Framework、Playwright等。本文将介绍一款在Python环境下的mechanize库&#xff0c;这个库能够模拟浏览器行为&#xff0c;支持发送HTTP请求、解析HTML页面和…

【2024.6.23】今日 IT 速递 | 亚布力创新年会热点新闻盘点

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

Vue3+TypeScript项目实战——打造雨雪交加的智慧城市

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

leetcode 二分查找·系统掌握

题目&#xff1a; 题解&#xff1a; 在阶梯数达到某一值后已有的硬币数量就小于了阶梯可以装的硬币数量&#xff0c;根据题意可以使用~10~泛型查找出最后一个可以被填满的阶梯。对于这类型可以二分答案的题目关键在于二分答案的上下界&#xff0c;本题的下界就是1上界就是硬币…

内容安全复习 8 - 视觉内容伪造与检测

文章目录 研究背景内容伪造方法虚假人脸生成人脸替换属性编辑表情重演跨模态人脸编辑 伪造检测方法眨眼检测交互式人脸活体检测一些了解方法挑战 研究背景 图像内容篡改造成新闻报道的偏颇易导致社会和公共秩序的不安&#xff0c;对公共安全产生不良影响。 造成的影响&#x…

英伟达能保住全球市值第一的桂冠吗?

内容提要 《巴伦周刊》认为&#xff0c;英伟达市值的迅速上涨是该公司可能难以保持市值第一桂冠的关键原因。另一个担忧是&#xff0c;英伟达的崛起主要基于一项单一技术——为人工智能应用提供动力的芯片和平台。一些人担心&#xff0c;如果购买英伟达产品的公司无法从投资中…

Open MMLab 之 MMDetection框架

MMDetection框架入门教程&#xff08;完全版&#xff09;-CSDN博客 OpenMMLab MMDetection是商汤和港中文大学针对目标检测任务推出的一个开源项目&#xff0c;它基于Pytorch实现了大量的目标检测算法&#xff0c;把数据集构建、模型搭建、训练策略等过程都封装成了一个个模块…

域名防红程序网站源码-最新可用

网上泛滥的都是2.5的版本&#xff0c;这是2.7的版本&#xff01; 功能简介 解决QQ内报毒问题&#xff0c;直接跳浏览器操作&#xff0c;好像这个版本只能安卓QQ了&#xff0c;最新版的支持IOS QQ。 url.cn 大绿标功能&#xff01;此源码仅供测试使用&#xff01; 安装说明 …

如何获取特定 HIVE 库的元数据信息如其所有分区表和所有分区

如何获取特定 HIVE 库的元数据信息如其所有分区表和所有分区 1. 问题背景 有时我们需要获取特定 HIVE 库下所有分区表&#xff0c;或者所有分区表的所有分区&#xff0c;以便执行进一步的操作&#xff0c;比如通过 使用 HIVE 命令 MSCK REPAIR TABLE table_name sync partiti…

【C语言】关于字符串函数的使用及模拟实现(1)

一、字符串追加 1.1 库函数srecat的使用 1.2 库函数strncat的使用 1.3 模拟实现库函数 strcat 及 strncat 由上可知&#xff0c;字符串追加的原理是找到所添加字符串的 \0 位置&#xff0c;再对其进行添加。 代码1、 代码2、 二、字符串查找 2.1 库函数strstr的使用 使用…

Day28:回溯法 491.递增子序列 46.全排列 47.全排列 II 332.重新安排行程 51. N皇后 37. 解数独

491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情…

用含成员函数的类,分别输入和输出各对象中的时间(时:分:秒)

编写程序&#xff1a; 运行结果&#xff1a; 注意&#xff1a; &#xff08;1&#xff09;在主函数中调用两个成员函数时&#xff0c;应指明对象名(t1,t2)。表示调用的是哪一个对象的成员函数。t1.display()和t2.display()虽然都是调用同一个 display函数&#xff0c;但…

最长考拉兹序列

题目&#xff1a; 考虑如下定义在正整数集上的迭代规则&#xff1a; n n/2 (若n为偶数) n 3n1 &#xff08;若n为奇数&#xff09; 从13开始&#xff0c;可以迭代生成如下的序列&#xff1a; 13 40 20 10 5 16 8 4 2 1 可以看出这个序列&#xff08;从13…

pytest测试框架pytest-xdist插件并发执行测试用例

Pytest提供了丰富的插件来扩展其功能&#xff0c;本章介绍下插件pytest-xdist&#xff0c;主要是提供并行测试、分布式测试、循环测试等功能&#xff0c;可以加快测试速度。 pytest-xdist官方显示没有严格的python和pytest版本限制。 pytest-xdist安装 使用pip命令安装: pip…

高中数学:数列-等差数列、等比数列的和与通项公式的关系

一、等差数列 1、通项公式与求和公式 2、性质 性质1 求和公式比上n&#xff0c;依然是一个等差数列。 性质2 等差数列中&#xff0c;每相邻m项和&#xff0c;构成的数列&#xff0c;依然是等差数列&#xff0c;公差&#xff1a;m2d 二、等比数列 1、通项公式与求和公式 a…

「GPT源码探索」:从ChatPaper到学术论文GPT的二次开发实践

前言 本文的前两个部分最早是属于此旧文的《学术论文GPT的源码解读与微调&#xff1a;从ChatPaper到七月论文审稿GPT第1版》&#xff0c;但为了每一篇文章各自的内容更好的呈现&#xff0c;于是我今天做了以下三个改动 原来属于mamba第五部分的「Mamba近似工作之线性Transfor…

数据库原理与安全复习笔记

1 概念 产生与发展&#xff1a;人工管理阶段 → \to → 文件系统阶段 → \to → 数据库系统阶段。 数据库系统特点&#xff1a;数据的管理者&#xff08;DBMS&#xff09;&#xff1b;数据结构化&#xff1b;数据共享性高&#xff0c;冗余度低&#xff0c;易于扩充&#xff…

自动控制原理出射角计算

背景&#xff1a;突然发现自己出射角不会算 被减数是零点到极点的角度&#xff0c;减数是极点到极点的角度