C++初阶引用

news2024/11/15 9:38:33

目录

    • 引用
      • 引用的特性
      • 使用输出型参数
      • 作返回值
      • 小总结
      • 引用的权限
      • 引用和指针

引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

比如周树人,在外的笔名叫鲁迅

类型& 引用变量名(对象名) = 引用实体;

int main()
{
	int a = 0;
	int& b = a; // 引用

	cout << &a << endl;
	cout << &b << endl;

	b++;
	a++;

	return 0;
}

在这里插入图片描述

引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
int a = 0;
int& b = a
int x = 1;
// 赋值
b = x;

在这里插入图片描述
b还是和a的地址是一样的

使用输出型参数

二叉树的前序遍历

本题用c语言写的话:

void _preorderTraversal(struct TreeNode* root,int* a,int* pi)
{
    if(root==NULL)
    return NULL;

    a[(*pi)++]=root->val;
    _preorderTraversal(root->left,a,pi);
    _preorderTraversal(root->right,a,pi);
}

而c++用了引用后:

void _preorderTraversal(struct TreeNode* root, int* a, int& ri)
{
    if (root == NULL)
        return;

    printf("[%d] %d ", ri, root->val);
    a[ri] = root->val;
    ++ri;
    _preorderTraversal(root->left, a, ri);
    _preorderTraversal(root->right, a, ri);
}

//只放部分代码展示
int i = 0;
_preorderTraversal(root, a, i);

为什么不是直接传值(如图):
在这里插入图片描述
使用交换函数的使用也会方便许多

void swap(int& x1, int& x2)
{
    int tmp = x1;
    x1 = x2;
    x2 = tmp;
}
int main()
{
    int x = 0, y = 1;
    swap(x, y);
    
	return 0;
}

以前学习单链表尾插的时候

c语言二级指针的玩法

void PushBack(ListNode** pphead, int x)
{
    ListNode* newnode;
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {

    }
}

int main()
{
    ListNode* plist = NULL;
    PushBack(&plist, 1);
    PushBack(&plist, 2);
    PushBack(&plist, 3);

    return 0;
}

C++,引用的玩法

typedef struct ListNode {
    int val;
    struct ListNode* next;
}ListNode, *PListNode;

void PushBack(ListNode*& phead, int x)
//void PushBack(PListNode& phead, int x) 和上面代码一样的
{
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    // ...
    if (phead == NULL)
    {
        phead = newnode;
    }
    else
    {

    }
}

int main()
{
    ListNode* plist = NULL;
    PushBack(plist, 1);
    PushBack(plist, 2);
    PushBack(plist, 3);

    return 0;
}

plistnode是对struct Listnode*的typedef,也就是指针的typedef。plistnode代表结构体指针

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
如果传的是指针或者引用效率就会高很多,所以引用可以提高效率

作返回值

在这里插入图片描述

不是把n返回给ret,因为函数调用结束的时候,n已经销毁了,所以不敢拿n去返回。所以设定是会生成临时变量,可能会用寄存器也可能是其他方式。在返回前拷贝给临时变量。把n的值拷贝到寄存器,然后寄存器充当返回值,一般值小的时候。当n比较大的时候,会在栈帧中间部分提前压一块空间作返回值。

还有一种返回方式叫传引用返回
返回的是n的引用,也就是返回的是n
可能会出现问题,n销毁了还返回n的别名,类似于野指针
在这里插入图片描述

int& Count()
{
	int n = 0;
	n++;
	
	// ...
	return n;
}

int main()
{
	int ret = Count();
	cout << ret << endl;
	cout << ret << endl;
    return 0;
}

打印的结果可能是1,也可能是随机值,取决于这个栈帧销毁后空间会不会被置成随机值,得看环境,在vs下的结果是1 1

来看下面的代码会造成什么不一样的情况:

int& Count()
{
	int n = 0;
	n++;
	
	// ...
	return n;
}

int main()
{
	int& ret = Count();
	cout << ret << endl;
	cout << ret << endl;
	return 0;
}

在这里插入图片描述

cout<<ret是一个函数调用,流插入这个函数调用还是在count空间上,只是栈帧大小可能比count大或小,函数调用时定义一些变量的时候就会对比如原来n的位置进行覆盖。

第一次调用没有覆盖因为调用函数先传参。传参过去之后函数建立栈帧但是传的值不会受到影响。第二次调用的时候想去取值,就会发现值被覆盖了。

不一定会覆盖,如果变量定义在太前一般都会被覆盖。比如在n前面定义一个大的数组就可能不被覆盖了。那么两次打印结果就都为1了(vs下)。

int& Count()
{
    int a[1000];
	int n = 0;
	n++;
	return n;
}

再看一个情况

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}

int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :" << ret << endl;
}

可能是随机值可能是7,看栈帧销毁后空间会不会被置成随机值。

传引用返回让代码优化的例子:

C的接口设计
读取第i个位置的值
int SLAT(struct SeqList* ps, int i)
{
	assert(i < ps->size);
	// ...
	return ps->a[i];
}
修改第i个位置的值
void SLModify(struct SeqList* ps, int i, int x)
{
	assert(i < ps->size);

	// ...
	ps->a[i] = x;
}

CPP接口设计
读 or 修改第i个位置的值
int& SLAT(struct SeqList& ps, int i)
{
	assert(i < ps.size);
	// ...
	return (ps.a[i]);
}

int main()
{
	struct SeqList s;
	s.size = 3;
	// ...
	SLAT(s, 0) = 10;
	SLAT(s, 1) = 20;
	SLAT(s, 2) = 30;
	cout << SLAT(s, 0) << endl;
	cout << SLAT(s, 1) << endl;
	cout << SLAT(s, 2) << endl;

	return 0;
}

小总结

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

传引用传参(任何时候都可以用)
1、提高效率
2、输出型参数(形参的修改,影响的实参)

传引用返回(出了函数作用域对象还在才可以用)
1、提高效率
2、修改返回对象

引用的权限

在引用的过程中
权限可以平移
权限可以缩小
权限不能放大

int func()
{
	int a = 0;

	return a;
}

int main()
{
	const int& ret = func();

	const int a = 0;

	// 权限的放大
	// int& b = a;
	
	//int b = a; 可以的,因为这里是赋值拷贝,b修改不影响a

	// 权限的平移
	const int& c = a;

	// 权限的缩小
	int x = 0;
	const int& y = x;//x更改y也会改,y不能更改

	int i = 0;
	const double& d = i;

	return 0;
}

在这里插入图片描述

在c/c++中,double d = i;发生类型转换(提升)、截断等时会产生一个临时变量,不是把 i 直接给d,是给一个double类型的临时变量,
double& d = i;是不行的原因是临时变量具有常性,这是一种权限的放大,用const引用临时变量就可以了。

在这里插入图片描述
返回的不是a,是a的拷贝,右边会报错是因为临时变量具有常性
用const引用临时变量就可以了const int& ret = func();
不用担心临时变量销毁,用const引用后会延长对象的生命周期,相当于ret出了作用域临时变量才销毁

引用和指针

语法上理解引用不开空间
下层到底是怎么样的?

在这里插入图片描述

lea是取地址,对a取地址放到寄存器,再把寄存器放到p1
从图可以看到引用和指针在底层是一样的,引用也是存地址

再看看使用的时候:
在这里插入图片描述
所以底层只有指针,没有引用
像正版和盗版,内核是一样的,但是可以通过品牌区分出来

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求,没有NULL引用,但有NULL指针
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

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

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

相关文章

毕马威加入IBM量子网络,已搭建完成通信行业量子电路!

​ 毕马威全球量子中心主管Bent Dalager&#xff08;图片来源&#xff1a;网络&#xff09; 随着全球企业技术的持续转型&#xff0c;量子计算在开发创新解决方案上越来越重要。为此&#xff0c;毕马威(KPMG)公司加入了IBM量子网络&#xff0c;旨在利用量子计算解决当今最复杂的…

为什么list.sort()比Stream().sorted()更快?

真的更好吗&#xff1f; 先简单写个demo List<Integer> userList new ArrayList<>();Random rand new Random();for (int i 0; i < 10000 ; i) {userList.add(rand.nextInt(1000));}List<Integer> userList2 new ArrayList<>();userList2.add…

Python爬虫遇到重定向问题解决办法汇总

在进行Python爬虫任务时&#xff0c;遇到重定向问题是常见的问题之一。重定向是指在发送请求时&#xff0c;服务器会返回一个新的URL&#xff0c;将请求重新定向到该URL。为了帮助您解决这个问题&#xff0c;本文将提供一些实用的解决办法&#xff0c;并给出相关的代码示例&…

java linq多字段排序时间比较

public static void main(String[] args) {//100万条数据List<CrmInvestSaleUserCount> waitAssignUserList new ArrayList<>();for (int i 0; i < 1000000; i) {waitAssignUserList.add(new CrmInvestSaleUserCount().setSales_username("test" i…

小孩学python课程需要多久,儿童学python哪个机构好

本篇文章给大家谈谈小孩学python课程需要多久&#xff0c;以及儿童学python语言能做什么&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 学习儿童python编程越来越受欢迎&#xff0c;原因有很多&#xff0c;对于孩子来说&#xff0c;Python是一种很好的编程语…

深入探究DDD领域建模的方法:从概念到实践

目录 &#xff11;.什么是领域建模&#xff1f; &#xff12;.领域驱动设计的基本原则 &#xff13;.领域建模的核心概念 &#xff14;.DDD领域建模方法 &#xff15;.领域建模中的挑战与解决方案 &#xff16;.DDD领域建模的优势和适用场景 &#xff17;.DDD领域建模的…

【枚举+01 trie树】CF1658 D2

Problem - D2 - Codeforces 题意&#xff1a; 给定一个区间[l, r]和r - l 1个数&#xff0c;问是否存在一个数 x &#xff0c;使得这些数异或上 x 之后为[l, r]的一个排列 思路&#xff1a; 这种有关一个集合和异或操作的&#xff0c;都可以试试字典树 我们将所有数插入到…

C++ 左值和右值

C 左值和右值 左值、右值左值引用、右值引用std::move()std::move()的实现引用折叠 完美转发forward()的实现函数返回值是左值还是右值如何判断一个值是左值还是右值 左值、右值 在C11中所有的值必属于左值、右值两者之一&#xff0c;右值又可以细分为纯右值、将亡值。在C11中…

Python实现蚁群优化算法,求解旅行商问题

文章目录 蚁群算法蚂蚁的基本变量蚂蚁的优化流程蚁群优化验证与可视化 蚁群算法 蚁群算法是Colori A等人在1991年提出的&#xff0c;通过模仿蚂蚁觅食行为&#xff0c;抽象出信息素这一奖惩机制&#xff0c;从而赋予蚂蚁智能Agent的身份&#xff0c;使之得以在最佳路线问题中大…

Windows环境下Node.js二进制版安装教程

文章目录 前言一 下载Node.js二 设置环境变量三 配置全局安装和缓存路径四 设置仓库 前言 新版的Node.js已自带npm&#xff0c;就在Node.js下载完成解压后的文件内&#xff0c;的node_modules包中。 npm的作用&#xff1a;是对Node.js依赖的包进行管理&#xff0c;类似maven。…

使用 CausalPy 进行因果推理

这篇文章通过一个实际的例子简要介绍了因果推理&#xff0c;这个例子来自于《The Brave and True》一书&#xff0c;我们使用 CausalPy 来实现。 因果推理是从观察数据中估计因果效应的过程。对于任何给定的个体&#xff0c;我们只能观察到一种结果。另一种结果对我们来说是隐藏…

蓝牙ble tips2-UUID GATT(service和CHARACTERISTIC) profile相关概念介绍

服务和特性 低功耗蓝牙设备之间通信&#xff0c;都是基于服务和特性。一个蓝牙设备中可以包含若干个服务&#xff0c;一个服务中可以包含若干个特性&#xff0c;每一个服务或者特性都要有一个UUID。蓝牙的数据交互都是基于一个个特性进行的&#xff0c;数据交互的方式有五种&a…

超全面的高精度行星减速机结构、原理、功能以及优势解析

行星减速机是运动控制系统中连接伺服电机和应用负载的一种机械传动组件&#xff0c;具有高减速比、良好的传动特性、结构紧凑、体积小、重量轻、可靠性高、低噪音等优点&#xff0c;广泛用于电机、汽车、机器人及各种工业机械中&#xff0c;以实现高效率和稳定性。 一、什么是…

Ubuntu20.04安装ROS Noetic 版本安装记录

用的是VMWare的20.04的Ubuntu虚拟机&#xff0c;打算安装一下Noetic 版本的ROS学习一下。B站有个视频可以参考一下&#xff1a;在Ubuntu20.04上安装ROS机器人操作系统-Noetic但是基本也是参考中文文档安装&#xff0c;步骤相比网上的教程更权威Ubuntu install of ROS Noetic&am…

LeetCode--HOT100题(20)

目录 题目描述&#xff1a;48. 旋转图像&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;48. 旋转图像&#xff08;中等&#xff09; 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#x…

echarts图表渐变色 + 每个柱子不同颜色设置

echarts柱状图&#xff0c;默认所有柱子都是同一个颜色&#xff0c;显示效果差强人意&#xff0c;本文介绍如果修改成为每个柱子添加不同的颜色&#xff0c;以及如何添加渐变色&#xff0c;丰富图表的显示鲜果。先看效果&#xff1a; 每个柱子颜色不同 每个柱子都有自己的渐变…

【论文阅读】基于 NeRF 的 3D 重建的批判性分析

【论文阅读】基于 NeRF 的 3D 重建的批判性分析 Abstract1. Introduction2. The State of the Art2.1. Photogrammetric-Based Methods2.2. NeRF-Based Methods 3. Analysis and Evaluation Methodology3.1. Proposed Methodology3.2. Metrics3.3. Testing Objects 4. Comparis…

HTML5(H5)的前生今世

目录 概述HTML5与其他HTML的区别CSS3与其他CSS版本的区别总结 概述 HTML5是一种用于构建和呈现网页的最新标准。它是HTML&#xff08;超文本标记语言&#xff09;的第五个版本&#xff0c;于2014年由万维网联盟&#xff08;W3C&#xff09;正式推出。HTML5的前身可以追溯到互联…

[MAUI 项目实战] 手势控制音乐播放器: 动画

吸附动画 还记的上一章节所描述的拖拽物&#xff08;pan&#xff09;和坑&#xff08;pit&#xff09;吗&#xff1f;“”吸附“”这是一个非常拟物的过程&#xff0c;当拖拽物品接近坑区域的边缘时&#xff0c;物体就会由于重力或是引力作用会滑落&#xff0c;吸附在坑里。 …

MINIO安装(centos7)

步骤1&#xff1a;安装wget 在开始安装MinIO之前&#xff0c;需要安装wget命令行工具。可以使用以下命令在CentOS系统中安装wget&#xff1a; sudo yum install wget 步骤2&#xff1a;下载MinIO wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio 将下…