【C++】C++引用 (引用不会?详细解决引用诸多细节!)

这篇具有很好参考价值的文章主要介绍了【C++】C++引用 (引用不会?详细解决引用诸多细节!)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++引用目录:

一、引用的概念

二、引用的特性

2.1引用在定义的时候必须初始化

2.2一个变量可以有多个引用

2.3引用一旦引用了一个实体,就不可以再引用其他实体

三、常引用(带const的引用)

3.1临时变量有常性不能修改(传值返回,隐式/强制类型转换时产生)

3.2指针/引用在赋值中,权限可以缩小,但是不能放大

3.3常引用做参数

3.4缺省参数如何引用?

四、引用的使用场景

4.1做参数(减少拷贝提高效率,实形一体)

4.2做返回值(减少拷贝提高效率,修改返回值)

4.2.1内存空间销毁意味着什么?& 访问销毁的内存空间会怎样?

4.2.2引用做返回值提高效率

4.2.3引用做返回值可被修改

五、引用和指针的区别


小知识:vscode查看汇编代码:

在调试控制器的位置输入:

-exec disassemble /m

一、引用的概念

基本形式为:类型& 引用变量名(对象名) = 引用实体

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

引用的符号和我们C语言中取地址的符号一样,为 &;在某类型名的后面加上引用符号 (&) 就变为了引用类型。设计引用的目的是简化指针的使用,但是引用不能代替指针 (实际上引用的底层是用指针实现的)

难点正是符号与地址符相同,比如见到*&就蒙圈,而且一定得记住在类型后面的都不是取地址,都是起别名

void TestRef()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++a和ra全部指向这个空间

注意:引用类型必须和引用实体是同种类型的(这个原因在常引用的权限放大与缩小会讲解原因)


二、引用的特性

2.1引用在定义的时候必须初始化

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

注意:const int&b=10;这个也是可以通过的,这个也算是初始化(至于为什么加const请看下面常引用的知识)

2.2一个变量可以有多个引用

int a = 10;
int& b = a;
int& c = a;
int& d = a;

此时,b、c、d都是变量a的引用

2.3引用一旦引用了一个实体,就不可以再引用其他实体

这句话的意思也间接表明,C++引用代替不了指针,因为指针是可以更改链接地址的

(在Java和python中,引用是可以更改的)

	int a = 10;
	int& b = a;

此时,b已经是a的引用了,b不能再引用其他实体。如果你写下以下代码,想让b转而引用另一个变量c:

	int a = 10;
	int& b = a;
	int c = 20;
	b = c;//你的想法:让b转而引用c

但该代码并没有随你的意,该代码的意思是:将b引用的实体赋值为c,也就是将变量a的内容改成了20


三、常引用(带const的引用)

常引用就是在引用类型前面用 const 来进行修饰;和 const 修饰变量一样,被 cosnt 修饰的引用只能够读,而不能够写,即不能修改引用变量的值

3.1临时变量有常性不能修改(传值返回,隐式/强制类型转换时产生)

C++ 中的临时变量指的是那些由编译器根据需要在栈上产生的,没有名字的变量。主要的用途主要有两类,临时变量都是具有常性的

1) 函数的返回值

这个图片很重要,一定得看:

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

 我们再举一个不用引用接收的例子:

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

在C++中,如果一个函数返回一个临时变量(通过传值返回),那么这个临时变量是一个右值,它不能被修改。`Add(a,b)` 返回一个临时变量,因此不能直接对其进行赋值操作。如果想要修改结果,可以将其存储在一个变量中,然后对变量进行操作。

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

int main() {
    int a = 0;
    int b = 3;
    int result = Add(a, b);
    result += 1;
    return 0;
}

我还是有个小问题,为什么const类型可以传给int类型的呢??(单纯赋值权限不说放大放小)

只有引用和指针需要考虑,其他的没问题的,所以在这里面就是最单纯的:

int给const错(因为const只可以在定义的时候初始化)

const给int对(可以将一个const类型的值赋给一个非const类型的变量,不能将一个非const类型的值赋给一个const类型的变量,因为这会破坏了const的只读性质)

但在下面的引用赋值的时候,小心混淆!

int& a=10 这是const给int 是权限放大 这是不对的(原来const只读,结果int鬼子入侵,当然不行)

int b=2;const int& c=b; 这是int给const 是权限放小 合理(原来我大大咧咧,然后被妻管严)

2) 类型转换时的中间变量

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

int main()
{
	int d=10;

	int i=(int)d;//强制类型转换,并不是改变了变量d,而是产生临时变量
	int i=d;//隐式类型转换,也是产生了临时变量

	double d=12.34;
	const int& ri=d;//这里引用的实体其实就是从double d 到int类型转换中间的临时变量
	return 0;
}

把这张图吃透,const关于临时变量的问题就可以告一段落了。题外话结束,进入正题

当然,这段代码是不可以运行的,因为有大量的重定义

3.2指针/引用在赋值中,权限可以缩小,但是不能放大

int main()
{
	const int a = 10;
	//int& ra = a;    //该语句编译时会出错,a为常量,权限放大
	const int& ra = a;//正确,权限平移
	
	//int& b = 10;    //该语句编译时会出错,10为常量,权限的放大
	const int& b = 10;//正确,权限平移

    int c=10;
    const int& rc=c;  //正确,权限缩小
	return 0;
}

权限只能被缩小和平移,不能被放大

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

注: 这里的权限指的是读和写的权限,且只针对于指针和引用

3.3常引用做参数

a.一般引用做参数都是用常引用,也就是const+引用,如果不用const会有可能产生权限放大的问题,而常引用既可以接收只读的权限,又可以接收可读可写的权限。

b.常引用做参数并不是为了修改参数,而是为了减少拷贝提高效率。

当然,并不是所有的参数加const就好,比如我们的swap函数,如果加上const反而会适得其反

3.4缺省参数如何引用?

缺省参数如果想做为引用的话,必须用常引用,因为缺省参数是一个常量,是不允许被修改的,只可以读

void func(const int& N = 10)
{

}

四、引用的使用场景

4.1做参数(减少拷贝提高效率,实形一体)

在调用函数时,形参是要做拷贝的,在它所在的函数栈帧里面,所以如果你要是传值调用,那必然在调用函数时,会做一份实参的临时拷贝,如果你是传址调用,指针变量也要开辟自己的空间,所以这些都是对程序性能的消耗

引用可以直接改变实参,作为输入性参数及输出型参数可以不再传递指针;比如下面的 Swap 函数,我们在 Swap 函数内部交换的其实就是两个实参的值,不用再像以前一样需要传递实参的指针

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

但如果我们用引用做参数就不会有这些问题了,因为操作系统并不会给引用变量单独开辟一块空间,并且引用变量也不需要对实参进行拷贝,那就可以减少拷贝提高效率。
并且由于引用实质上就是实参本身,那么它也可以作为输出型参数,对函数外面的实参进行修改

//单链表
typedef int SLTDataType;  //数据类型重命名
typedef struct SListNode   //链表的一个节点
{
	SLTDataType data;
	struct SListNode* next;  //存放下一个节点的地址
}SLTNode;

//在头部插入数据
void SListPushFront(SLTNode*& rphead, SLTDataType x)  //引用做形参,直接操作plist
{
	SLTNode* newNode = BuySLTNode(x);  //开辟新节点
	newNode->next = *rphead;
	*rphead = newNode;
}

在单链表的头插等操作,由于我们之前实现的单链表是不带头的,所以我们需要传递 plist 的指针,方便插入第一个数据时改变 plist,而现在,我们可以直接使用 plsit 的引用即可,不用再传递二级指针了,从而使代码变得更易理解。当然,我们不能把 SListNode 中的 next 指针也设计为引用,因为尾结点的 next 是在不断改变的,而引用一旦引用一个实体,就不能再引用其他实体,这也从侧面说明了引用不能代替指针,只能简化指针

4.2做返回值(减少拷贝提高效率,修改返回值)

我们先来探讨局部变量空间销毁的问题

4.2.1内存空间销毁意味着什么?& 访问销毁的内存空间会怎样?

内存空间销毁并不是把这块内存空间撕烂了,永久性的毁灭这块儿空间,内存空间是不会消失的,他会原封不动的在那里,只不过当内存空间销毁之后,他的使用权不属于我们了,我们存到里面的数据不被保护了,有可能发生改变了

销毁之后,我们依然可以访问到这块空间,只是访问的行为是不确定的,我们对空间的数据进行读写的行为是无法预料的

把空间当作酒店,把数据当作苹果:
a.苹果没丢    b.房间没丢,但是成了随机水果    c.苹果被覆盖成固定水果

示例一:

int& Func()
{
	static int n = 0;
	n++;
	printf("&n:%p\n", &n);
	return n;
}

int main()
{
	int ret = Func();
	cout << ret << endl;
	printf("&ret:%p\n", &ret);

	int& rret = Func();
	printf("&rret:%p\n", &rret);
	return 0;
}

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

n的地址和rret相同,说明引用返回的是变量n本身,而不仅仅是n的值

在 Func 函数中,n 由于是静态变量,而静态变量在静态区中开辟空间,不在栈区上开辟空间,所以当 Func 函数的栈帧销毁后 n 并不会被销毁

同时,我们用引用做返回值,相当于直接将 n 这个变量返回给了函数调用者,所以,当我们再用一个引用来接收的话,就等价于给 n 再起了一个别名;

示例二:(注:下面对引用的使用方式有部分是错误示范,一定得跟着博主一起慢慢吃透)

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

分析:对于左侧代码,离开Count函数栈帧,变量n会被销毁,返回的别名指向的是一块被销毁后的变量空间名,这时如果使用返回后的别名,出现的结果是无法预料的(上方举的apple例子),由于n变量被销毁,所以一定不可以用引用返回(但是不会有语法编译错误,一定得小心)

对于右侧代码,变量n不会被销毁,存放在的是静态区,所以可以使用引用返回

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

分析:函数都已经被销毁,返回值为什么还可以被带出来呢?是因为在执行return语句前,系统会自动创建一个临时变量temp,这个变量的存储地址是根据return内容的大小分配的,可能是寄存器,也可能是堆,我们无法预知,接着操作系统会把即将要被释放的变量的值赋值给这个临时变量,通过临时变量将返回值带出来

#include<iostream>
using namespace std;

int& Count()
{
	int n = 0;//n存放在栈区
	n++;
	//...
	return n;
}
void Func()
{
	int x=100;
}
int main()
{
	int &ret = Count();
	cout << ret << endl;

	cout << ret << endl;

	Func();
	cout << ret << endl;
	return 0;
}

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

分析:

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

1、Func 函数中的 n 变量是局部变量,函数调用完成后被销毁,但是我们这里是引用做返回值,所以返回的是 n 那块栈区空间;

2、ret 是Func 函数返回值的引用,而函数返回值是局部变量 n 的引用,所以我们第一次打印的是 n 的值1;

3、我们在打印 ret 的时候调用了 printf 函数,该函数使用了Count被销毁的空间,所以 n 空间中原本的数据被覆盖,而 ret 是 n 的引用,所以打印 ret 就是打印 printf 函数中原本属于 n 的那块内存空间的数据,所以打印出来的是一个随机值。

4、我们调用了Func函数,然后发现Func中的 x 变量的地址和原本 n 的地址相同,说明 Cover 函数又使用了 printf 函数的空间,且 x 的空间恰好位于之前 n 的位置,所以打印 ret 的结果为100;

空间销毁的问题解决完毕,继续给出一段引用做返回值的错误代码,继续理解分析:

4.2.2引用做返回值提高效率

根据上面传值返回得知会创建临时变量,所以传引用返回提高效率是显而易见的

4.2.3引用做返回值可被修改

下面这段代码给大家演示了C++中利用引用作为返回值来修改返回值的场景。
将数组中的偶数全部扩大二倍

int& change(int* arr,int i)
{
	return arr[i];
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	for (int i = 0; i < 10; i++)
	{
		if (arr[i] % 2 == 0)
		{
			change(arr, i) *= 2;
		}
	}
	for (int i = 0; i < 10; i++)
	{
		cout << arr[i] << " ";
	}
}

总结:

出了函数作用域返回变量不存在了,不能用引用返回因为引用返回的结果是未定义的
出了函数作用域返向变量存在,才能用引用返回


五、引用和指针的区别

语法概念上:引用就是一个别名,没有独立空间,和其引用实体共用同一块空间

底层实现上:引用实际是有空间的,因为引用是按照指针方式来实现的

我们调试代码,然后转到反汇编后可以发现,引用和指针的汇编代码是完全一样的,即引用的底层实际上就是指针

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

引用和指针的不同点(9大点)

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

【C++】C++引用 (引用不会?详细解决引用诸多细节!),小阳c++专栏,c++

希望这篇费劲心思的c++引用全文可以对你们有所帮助!!! 加油!!文章来源地址https://www.toymoban.com/news/detail-708853.html

到了这里,关于【C++】C++引用 (引用不会?详细解决引用诸多细节!)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++学习手札】-引用与内联函数以及C++中对const拓展详解(超详细!)

                                           食用指南:本文在有C基础的情况下食用更佳                                        🔥 这就不得不推荐此专栏了:C语言                                        🍀 内联函数前置知识 :宏函数

    2024年02月14日
    浏览(49)
  • C++ 引用(&)的超详细解析(小白必看系列)

    目录 一、前言  二、引用的概念介绍 三、引用的五大特性   💦  引用在定义时必须初始化  💦 一个变量可以有多个引用   💦 一个引用可以继续有引用 💦 引用一旦引用一个实体,再不能引用其他实体  💦 可以对任何类型做引用【变量、指针…】  四、引用的两种

    2024年02月03日
    浏览(49)
  • 解析C++链接错误:未定义引用和未解析符号的完整解决方案

      概述: C++中的\\\"未定义的引用\\\"和\\\"未解析的外部符号\\\"错误通常源于声明与定义不一致或缺失定义。解决方法包括确保所有声明有相应定义、检查定义位置,使用头文件和命名空间组织代码。这些步骤帮助开发者提高代码可维护性和避免链接错误。 在C++中,\\\"未定义的引用\\\"和

    2024年03月20日
    浏览(50)
  • 【C++】C++ 引用详解 ⑦ ( 指针的引用 )

    指针的引用 效果 等同于 二级指针 , 因此这里先介绍 二级指针 ; 使用 二级指针 作为参数 , 可以实现如下功能 : 动态内存管理 : 借助二级指针 , 可以在函数中分配或释放内存 ; 如 : 创建一个动态数组或调整现有数组的大小 , 在函数中需要一个指向指针的指针作为参数 , 以便修

    2024年02月11日
    浏览(37)
  • 【C++】C++ 引用详解 ⑧ ( 普通引用与常量引用 | 常量引用概念与语法 )

    之前的 【C++】C++ 引用详解 ① ~ ⑦ 博客中 , 讲解的都是 普通引用 , 也就是 将 普通变量 赋值给 引用 , 过程如下 : 先定义 普通变量 a , 然后定义 已存在变量 a 的引用 b ; 这里的 引用 b 就是 普通引用 ; 普通引用代码示例 : 执行结果 : 与 变量引用 相对的就是 常量引用 ; 通过 普

    2024年02月11日
    浏览(33)
  • 【C++】C++ 引用详解 ① ( 变量的本质 - 引入 “ 引用 “ 概念 | 引用语法简介 | 引用做函数参数 | 复杂类型引用做函数参数 )

    \\\" 引用 \\\" 语法 是 C++ 语言中 特有的 , 在 C 语言中是没有 引用 这个概念的 ; 分析 引用 之前 , 先回顾下 变量 : 在 【C 语言】变量本质 ( 变量概念 | 变量本质 - 内存空间别名 | 变量存储位置 - 代码区 | 变量三要素 ) 博客中 , 介绍了变量的本质 : 变量 的本质是 内存空间 的 \\\" 别名

    2024年02月11日
    浏览(48)
  • 【C++初阶】C++入门——引用

    前言:  相信大家在学习C语言的时候,最头疼的就是指针,经常会碰到一级指针、二级指针,这些指针使用起来,稍有不慎就会等导致程序崩溃,为了让广大程序员少掉点头发,C++中提出了 引用 这一概念。当然,在C++的代码中,仍然可以兼容C语言的指针。  在语法上 引用

    2024年02月13日
    浏览(38)
  • Python 和 C++ 使用细节差别

    C++ 中, for 循环中写明 a.size() ,每次循环这个值是重新计算的; Python 中 for 循环 len(a) , 循环中a变化r循环仍采用刚开始执行循环时计算的len(a) while 循环,python3和C++,while条件中的长度计算是可变的,即每次循环都会重复计算。 #include algorithm list.insert(idx, val) idx 整数 vecto

    2024年02月11日
    浏览(43)
  • 回炉与剖析C++封装特性 - 重新认识C++,完满呈现全部内部细节

    💛前情提要💛 本章节是 C++ 的 深度剖析封装细节特性 的相关知识~ 接下来我们即将进入一个全新的空间,对代码有一个全新的视角~ 以下的内容一定会让你对 C++ 有一个颠覆性的认识哦!!! 以下内容干货满满,跟上步伐吧~ 作者介绍: 🎓 作者: 热爱编程不起眼的小人物🐐

    2023年04月13日
    浏览(34)
  • 【C++】C++入门基础:引用详解

    本篇继续分享关于C++入门的相关知识,有关命名空间、缺省参数和函数重载的部分欢迎阅读我的上一篇文章【C++】C++入门基础详解(1)_王笃笃的博客-CSDN博客 继续我们的学习 在C语言中我们接触过指针,很多人都或多或少为他感到头痛过,很多C语言的使用者包括创始人都觉

    2024年02月10日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包