《C++程序设计》阅读笔记【5-引用】

在这里插入图片描述

在这里插入图片描述

🌈个人主页:godspeed_lucip
🔥 系列专栏:《C++程序设计》阅读笔记

本文对应的PDF源文件请关注微信公众号程序员刘同学,回复C++程序设计获取下载链接。


  • 1 引用
    • 1.1 概念
    • 1.2 和引用相关的操作
      • 1.2.1 什么能被引用
    • 1.3 用引用传递函数参数
    • 1.4 用引用返回值
    • 1.5 const限定引用
    • 1.6 返回堆中变量的引用
  • 2 总结


1 引用

1.1 概念

引用作为目标的别名而使用,对引用的改动实际就是对目标的改动。

例如:rInt就是someInt的引用

int someInt;
int  &rInt = someInt;
//int &rInt、int& rInt、int & rInt都是等价的

引用不是值,不占存储空间,声明引用时,目标的存储状态不会改变。

引用只有声明,没有定义(定义必然设计到分配具体空间)

引用在声明时必须被初始化,则会产生编译错误。例如:int &rInt是错误的

虽然引用运算符与地址操作符使用相同的符号(&),但它们不是一样的

&除了引用之外,其他任何时候使用都表示取地址操作符。例如int i=1; cout<<&i;是输出整形变量i的地址。

1.2 和引用相关的操作

引用的地址是被引用目标的地址

引用被建立后,实际上就不能让引用指向新的引用目标,因为对引用的操作就是对其引用目标的操作。请看下面的例子:

#include<iostream>
using namespace std;int main(){int origin = 1;int & r_origin = origin; //建立对origin变量的引用cout<<&origin<<endl;cout<<&r_origin<<endl; //输出两者的地址cout<<origin<<endl;cout<<r_origin<<endl; //输出两者的值int other = 2; //新建立一个int变量r_origin = other; //试图让引用指向新的引用目标cout<<origin<<endl;cout<<r_origin<<endl;cout<<other<<endl;//输出三者的值exit(0);
}

结果:

image-20240229135316766

从上面这个例子也可以看出引用和指针的差别:

引用不能改变指向,但是指针是可以改变指向的(指针常量除外)

1.2.1 什么能被引用

  1. 若一个引用对象被声明为Type &r = value,则要求valueType类型,或者value可以被隐式转换为Type类型。

有待商榷,我的C++03版本的dev中,下面的代码是错误的:

int main(){int a = 1;double &b = a;return 0;
}
  1. 如果引用类型Type的初始值不是一个左值,那么将建立一个Type类型的目标并用初始值初始化,那个目标的地址变成引用的值。

事实上我感觉这条有待商榷。我的C++版本是C++03。下列代码都是错误的:

void test02() {double& r_dou_1 = 1;double& r_dou_2 = 1.0;
}

只有这样才是正确的:

void test02() {double sour = 1.0;double& r_dou_2 = sour;
}
  1. 指针也是一种数据类型,也可以被引用。例如:
void test03(){int b=1;int* a = &b;int* &point_r = a;cout<<*point_r<<endl;
}

结果:

image-20240229143646930

TIP:
int* a = &1;是错误的,只能像int b=1; int* a = &b;才可以。

因为1是一个常量右值,它没有一个明确的内存地址

  1. 不可以对void进行引用。例如
void& a=3; //error

void只是在语法上相当于一个类型,本质上不是类型,没有任何一个变量或对象的其类型为void

  1. 数组不能被引用

一方面,数组是某个数据类型元素的集合,每个元素皆为引用,意味着每个元素必须初始化为其他内存实体;并且数组的大小必须在编译时就确定,但是引用是在运行时才进行绑定的,因此引用的数组是错误的;

另一方面,数组名只是表示该元素集合空间的起始地址,若对其引用,那就是数组的别名,与指向数组的指针没有什么区别

其实好像也是可以引用的,例如:

int main(){int arr[] = {1, 2, 3, 4, 5};int (&ref)[5] = arr; // 引用数组cout<<ref<<endl;return 0;
}

其结果就是:

image-20240321124812610

  1. 不可以对引用再次引用,也不可以用指针指向引用

其实很好理解,引用本质上不是对象,不占用内存空间。被引用和指向的目标都必须是一个对象。

  1. 引用不可以使用类型来初始化

例如:

int &ra = int; //error

理解:引用是变量或者对象的引用,而不是类型的引用

  1. 空引用不能存在,如:int &r = NULL是错误的

1.3 用引用传递函数参数

因为引用就是变量的别名,传递引用就是在传递引用对象本身。

传递引用给函数与传递指针的效果一样,传递的是原来的变量或对象(事实上,在底层还是通过传递地址的方式实现的),而不是在函数作用域内建立变量或对象的副本。

例子:使用引用交换变量值

void swap(int &x,int &y){int temp = x;x = y;y = temp;
}void run_swap(){int a=0;int b=4;cout<<a<<endl;cout<<b<<endl;swap(a,b);cout<<a<<endl;cout<<b<<endl;
}int main(){run_swap();
}

结果:

image-20240229151853914

注意:下面的两个重载函数会报错:

image-20240229152116576

1.4 用引用返回值

请看以下例子:

image-20240229152917992

对于第一种情况:

fn1以值的形式返回。在返回全局变量temp时,编译器会在fn1函数的栈空间中,创建临时变量存储temp的值。之后再将该临时变量赋值给a

对于第二种情况:

fn1以值的形式返回。与第一种情况相同,编译器同样会创建临时变量。但是接受该变量的是一个引用,于是引用b的引用变量就变成了该临时变量。但是,由于临时变量是存储在fn1的栈空间中的,但是当fn1返回后,其数据就会被销毁,所以引用b就会指向一个无意义的数据。因此会出现warning。正确做法是这样的:

int  x = fn1(5.0);
int &b = x;

对于第三种情况:

fn2以引用的形式返回,因此不会创建数据副本,而是直接把c赋值为全局变量temp的值。

对于第四种情况:

以引用的形式返回,同时以引用的形式接受。这时候,引用d的目标对象就是temp

返回局部变量的引用是错误的(类似于返回局部变量的指针)。因为这可能导致对已释放内存的引用或失效的对象的引用,进而引发未定义行为。

1.5 const限定引用

简单来说,加上const之后,引用就只能被读,不能被修改

例如下面的代码:
image-20240229161955603

其结果为:(代码较简单,不详细解释)
image-20240229162013013

1.6 返回堆中变量的引用

小tip:

正确代码:

int *p =  new int(20);
int* &ref = p;

错误代码:

int* &ref = new int(20);

解释:原因是 new int(20) 返回的是一个右值(rvalue),而非左值(lvalue)。在C++中,非const引用无法绑定到右值,因为右值通常是临时的,不可修改。

考虑到new不一定能成功申请到内存,所以可能返回NULL,但是引用不可以是NULL。所以最好增加一个检查

释放内存:

在上述的例子中,ref是指针p的引用,因此可以使用delete refdelete p释放。

当然,假如代码是这样的:

int *p =  new int(20);
int &ref = *p;

那么,使用delete &ref(注意这里的&是取地址操作)或者delete p释放。

2 总结

C++,犹如编程的交响乐, 在代码的海洋中奏响和谐的旋律。

它是创造者的笔,雕刻着无尽可能,

是思想的翅膀,让梦想飞翔的天空。

无拘无束,灵活多变。

C++,是程序员心中的宝藏,永不凋零的花朵。

渴望挑战C++的学习路径和掌握进阶技术?不妨点击下方链接,一同探讨更多C++的奇迹吧。我们推出了引领趋势的💻C++专栏:《C++程序设计》阅读笔记,旨在深度探索C++的实际应用和创新。🌐🔍

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

WebKit结构揭秘:探秘网页渲染的魔法之源

一、WebKit之心&#xff1a;渲染引擎的魔力 WebKit的渲染引擎是其核心所在&#xff0c;它犹如一位技艺高超的魔法师&#xff0c;将HTML、CSS和JavaScript的魔法咒语转化为绚丽的网页画面。它解析代码&#xff0c;绘制页面&#xff0c;让网页内容跃然屏上&#xff0c;展现出无尽…

ROS 2边学边练(15)-- 写一个简单的服务(C++)

前言 此篇我们即将编写一个简单的服务&#xff08;service&#xff09;通信例子&#xff0c;客户端节点向服务端节点发出请求&#xff08;.srv文件中规定了通信的数据结构格式&#xff09;&#xff0c;服务端节点收到请求后将结果回复给客户端节点&#xff0c;一问一答&#xf…

Unity类银河恶魔城学习记录12-4 p126 Item Tooltip源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI.cs using System.Collections; using System.Collections.Generic; usi…

【面经】interrupt()、interrupted()和isInterrupted()的区别与使用

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;面经 ⛺️稳中求进&#xff0c;晒太阳 interrupt方法 如果打断线程正在sleep&#xff0c;wait&#xff0c;join会导致被打断的线程抛出InterruptedException&#xff0c;并清除打断标记。如…

Redis的配置文件详解

单位&#xff1a;Redis配置对大小写不敏感&#xff01; 注意这里&#xff1a;任何写法都可&#xff0c;不区分大小写。 units are case insensitive so 1GB 1Gb 1gB are all the same.包含&#xff1a;搭建Redis集群时&#xff0c;可以使用includes包含其他配置文件网络&…

关于 elf loader 的编写

可以使用如下命令观看 elf 文件的信息 readelf -a build/ramdisk.img | vim -在编写 elf loader 的时候&#xff0c;实际上只有下图这一部分 “Program Headers” 是有用的 凡是类型为 “LOAD” 的就是需要加载进内存的部分 所以&#xff0c;只要把这些部分加载进内存里&…

数据库不用mmap

你确定你想用 MMAP 实现数据库么&#xff1f;_哔哩哔哩_bilibili MMAP 的随机读与顺序读的性能表现不好&#xff0c;以及对于写主要是不可控的刷入时机以及代码冗余&#xff0c;所以 MMAP 不适合在数据库中使用。 mmap是posix系统调用&#xff0c;它提供由操作系统管理内存映…

(C)1008 数组元素循环右移问题

1008 数组元素循环右移问题&#xff1a; 问题描述 输入样例&#xff1a; 6 2 1 2 3 4 5 6 输出样例&#xff1a; 5 6 1 2 3 4 解决方案&#xff1a; #include<stdio.h> #include<string.h> #include<math.h> int main(){int n,k,flag,y,x,final;int a[10000…

Flutter Boost 3

社区的 issue 没有收敛的趋势。 设计过于复杂&#xff0c;概念太多。这让一个新手看 FlutterBoost 的代码很吃力。 这些问题促使我们重新梳理设计&#xff0c;为了彻底解决这些顽固的问题&#xff0c;我们做一次大升级&#xff0c;我们把这次升级命名为 FlutterBoost 3.0&am…

合理早餐选择,稳定糖尿病血糖。

对于糖尿病患者来说&#xff0c;饮食管理是治疗的重要一环。不合理的早餐选择会导致血糖的波动。很多糖尿病朋友按时吃药&#xff0c;但是血糖就是稳定不住&#xff0c;之前看过一个例子&#xff0c;北京崇文门医院朱学敏主任接诊过一个患者&#xff0c;那个患者按时吃药&#…

LaTeX 空格与换行

任意多个空格与一个空格的功能相同。只有字符后面的空格是有效的&#xff0c;每行最前面的空格被忽略。单个换行被视作一个空格&#xff0c;连续两个换行表示分段。~被称作一种不可打断的空格&#xff0c;排版系统不会在这种空格之间换行。西文的逗号、句号和分号等标点后面应该…

Java | Leetcode Java题解之第8题字符串转换整数atoi

题目&#xff1a; 题解&#xff1a; class Solution {public int myAtoi(String str) {Automaton automaton new Automaton();int length str.length();for (int i 0; i < length; i) {automaton.get(str.charAt(i));}return (int) (automaton.sign * automaton.ans);} …

Android Studio学习10——资源res的使用

一、String,StringArray的使用 一次修改&#xff0c;多出生效 String StringArray 二、color的使用 颜色代码对应表 和上面的相似用法 三、Dimen(尺寸)的使用 用的少&#xff0c;一般直接写尺寸 四、如何写一个drawable作为背景 五、如何写一个可以改变的drawable(按钮按下…

IP地址:是给主机配置的,还是给网卡配置的?

在探索网络的奥秘时&#xff0c;我们经常会遇到一个看似简单但又复杂的问题&#xff1a;IP地址到底是配置在主机上&#xff0c;还是配置在网卡上&#xff1f;为什么我们通常说的是“主机IP地址”呢&#xff1f;让我们一起深入探讨。 1. 网卡与IP地址 &#x1f5a5;️&#x1f…

利用OllyDbg对程序内容进行修改实验

1.双击运行exe文件&#xff0c;出现如下弹窗 2.用ollydbg工具打开该执行文件&#xff0c;页面显示如下 3.在注释窗口执行以下操作 4.双击运行exe文件时&#xff0c;显示”Copied!”所以接下来在注释里找到这个字样&#xff0c;如下&#xff0c;我们需要把对话框中的内容修改为“…

SQL语句学习+牛客基础39SQL

什么是SQL&#xff1f; SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统&#xff08;RDBMS&#xff09;。 SQL 的范围包括数据插入、查询、更新和删除&#xff0c;数据库模式创建和修改&#xff0c;以及数据访问控制。 SQL语法 数据库表 一个…

ChatGPT(3.5版本)开放无需注册:算力背后的数据之战悄然打响

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

如何在 Mac 上恢复已删除的数据

如果您丢失了 Mac 上的数据&#xff0c;请不要绝望。恢复数据比您想象的要容易&#xff0c;并且有很多方法可以尝试。 在 Mac 上遭受数据丢失是每个人都认为永远不会发生在他们身上的事情之一......直到它发生。不过&#xff0c;请不要担心&#xff0c;因为您可以通过多种方法…

Java数据结构队列

队列(Queue) 概念 队列的使用 注意&#xff1a;Queue是个接口&#xff0c;在实例化时必须实例化LinkedList的对象&#xff0c;因为LinkedList实现了Queue接口。 import java.util.LinkedList; import java.util.Queue;public class Test {public static void main(String[]…

【事务注解✈️✈️】@Transactional注解在不同参数配置下的功能实现

目录 前言 使用场景 1.单个方法层面 2.类级别使用 3.指定异常回滚 4.跨方法调用事务管理 5.只读事务 ​ 6.设置超时时间&#xff0c;超时则自动回滚 7.隔离级别设置 章末 前言 小伙伴们大家好&#xff0c;ACID&#xff08;原子性&#xff0c;一致性&#xff0c;隔离…