当前位置: 首页 > news >正文

C++___快速入门(下)(引用)

前言

        快速入门(上)篇的链接,本篇接上文。C++___快速入门(上)-CSDN博客

引用

        引用的概念和定义

        引用就是给已经存在的变量起一个别名,它和它引用的那个变量共用同一块内存空间。就比如像水浒传里边的李逵,又名“黑旋风”。

        引用的定义:类型& 引用别名 = 引用对象;

        由上图,b就是a的一个别名,如果不加引用的操作符 & ,那么就变成了赋值。

        引用的使用

        作用1:可以作为函数的形参。这个比较简单,我直接举一个例子。

#include<iostream>
using namespace std;//Swap函数用于交换两个变量里边的值
void Swap(int& x,int& y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 10;int b = 20;cout << "交换前" << a << " " << b << endl;Swap(a, b);cout << "交换后" << a << " " << b << endl;return 0;
}

        当函数的形参是实参的别名之后,改变形参即是改变实参。

        作用2:做函数形参,减少拷贝,提高效率。

#include<iostream>
using namespace std;//我们要知道的一个点就是这个结构点所占用的内存很大
struct A
{int arr[100];int b;//...............
};//写一个函数,将结构体A传参传过去
void test(struct A& aa)
{//..................
}int main()
{struct A a;test(a);return 0;
}

        我们都知道形参是实参的一份临时拷贝,而上边的那个结构体A所占用的内存非常的大,如果直接传过去,就可能会导致内存空间不足的问题,为了解决这个问题,就可以使用引用,这样形参和实参就共用同一块空间了。

        作用3:引用作为返回类型,修改返回对象。

        我们知道如果不是引用作为返回对象的话,临时变量会在函数调用结束之后随着函数一起销毁掉,而返回值会临时拷贝在寄存器里边返回回来,说白了就是把一个常量返回了回来,所以返回的对象此时是不能修改的。但用引用来操作的话就可以修改。

#include<iostream>
using namespace std;int& test1(int arr[],int i)
{//拿到数组下标为i的元素int a = arr[i];return a;
}//int test2(int arr[], int i)
//{
//	//拿到数组下标为i的元素
//	int a = arr[i];
//	return a;
//}int main()
{int arr[6] = { 1,1,1,1,1,1 };for (int j = 0; j < 6; j++){printf("%d ",test1(arr, j) += 1);}//err//for (int j = 0; j < 6; j++)//{//	printf("%d ", test2(arr, j) += 1);//}return 0;
}

        如上边注释的代码部分就是无法完成返回值的修改的,而上边代码里边的变量a虽然已经被销毁了,但由于是返回的是它的引用,就相当于 int* tmp = a; 这样类似的意思。而别名就是可以被修改的。(但这上边的代码其实还是有潜在的问题的,后边会说)

        作用4:引用做返回值类型,可以减少拷贝,提高效率。

        这个很好理解,就是返回的如果是一个很大的空间,那用普通的类型来接收的话就需要临时拷贝一份,占用内存,但是用引用类型的话,就是起了个别名而已,就不会再额外开辟空间。

        关于引用的作用3示例代码的潜在问题。

        来看一段代码。

#include<iostream>using namespace std;
int& func()
{int ret = 10;return ret;
}int main()
{int x = func();cout << x << endl;return 0;
}

        先从函数栈帧的角度来看一下,main函数会在栈区开辟一块空间,而变量x就在main函数的栈区有一块自己的空间,func函数里边的变量ret会随着func函数的调用返回结束之后销毁,这就导致ret函数里边的数据可能已经被计算机清理掉了。但由于func函数返回的是一个引用类型,把引用类型的赋值给x,就相当于是直接把ret变量赋值给x,此时的x接收的就是被销毁后的ret变量里边的值,在不同的编译器下,里边的值不确定。可能是随机值,也可能是原来的那个值。

        而上边引用的作用3里边test1函数里边的那个变量a其实也是会销毁的,可是后边main函数里边还在使用里边存储的值,这就会导致和这里的示例代码一样的问题。

        问题拓展:

        还是先上代码。

#include<iostream>using namespace std;
int& func()
{int ret = 10;return ret;
}int main()
{int& x = func();cout << x << endl;return 0;
}

        这里我用int&来接收func函数的返回值,此时的x是引用类型,就是函数func里边的变量ret的别名,现在x和ret的地址是一样的,你可以理解为这里的x和func里边的ret是同一个,就像酒店里边你如果开了一间房间,可是你已经退房了,却还有这间房间的钥匙,是很危险的一件事情。

引用的特性

        1.引用在定义时必须初始化。

        2.一个变量可以有多个引用

        3.引用一旦引用了一个实体,就不能再引用其它实体。(这也是引用和指针的一个最大的区别)

#include<iostream>
using namespace std;
int main()
{int a = 10;//a的多个引用int& b = a;//引用的初始化int& d = a;int& f = a;int c = 100;b = c;//这叫变量的赋值return 0;
}

const引用

        我们知道被const修饰的变量具有常性(不能理解为跟常量一样),也就是不能被直接的修改,但是可以通过指针去修改。而我们要给const修饰的变量取别名,就要用到const引用,直接看代码。

#include<iostream>
using namespace std;
int main()
{//以下两种都行const int a = 10;//int const a = 10;//以下两种方式都行const int& b = a;//int const& b = a;//如果直接用  int& b = a 的方式会报错,因为这就属于权限的放大//本来a是具有常性的,不能被修改,但是给它取了别名之后的b可以被修改了,这是不行的。//权限不能放大,但可以缩小int c = 100;const int& t = c;return 0;
}

        上边const的位置是没有影响的,但是const位置的变化对于指针是有影响的。关于权限的放大和缩小是只是在引用和指针里边有这个概念。看下边的伪代码。

const int a = 100;

int c = a;

        上边的代码并没有什么权限的说法,c是在栈上的一个跟y毫无关系的变量,只不过是y的值拷贝了一份给了c而已。而引用是取别名,是共用同一块的空间,当然是有权限的问题的。而指针同样的道理就是涉及到指向以及指向的内容的修改问题。

const引用还能给常量取别名 const int& a = 10;

        此外,关于临时变量,const引用也能起别名,临时变量(临时对象),就比如像一些表达式的求值结果,函数的返回值,编译器会主动给它们的求值结果创建一个未命名的变量来存放它们,这些变量称为临时变量,它们的特点就是具有常性。

Eg1:  int x = 10;

          const int& a = x * 10;

Eg2:  double b = 1.1;

          const int& c = b;

        这个Eg2本质上也是在给临时变量起别名,如果代码是 int c = b; 当b赋值给c的时候,其实编译器会先把b的整数部分给到一个临时变量,然后再将这个临时变量赋值给c,所以若要取别名,就要用到const引用。不然就是权限的放大。

        另外,改变Eg2里边的b是不会影响c的,因为c相当于是临时变量的引用,临时变量跟b已经没有什么关系了。 

        最后总结一下,上边的const引用是没有实际的作用的,实际的作用要在C++后边的内容里边。

http://www.dtcms.com/a/310494.html

相关文章:

  • Linux基础 -- 内核快速向用户态共享内核变量方案之ctl_table
  • 大模型学习思路推荐!
  • 基于K近邻的缺失值填补:原理、步骤与实战解析
  • Winform 中实现控件与数据的绑定,一方改变另一方同步改变。
  • 【Onvif从零实践】02、Onvif 测试工具(ONVIF Device Test Tool)的 安装、使用 教程
  • C++入门自学Day4-- c++类与对象(友元)
  • JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
  • 水果忍者经典版:离线版,永久无限制!!
  • IPD数字化的困难与解法
  • 如何在 VMware Workstation 虚拟机中利用 Nvidia 显卡的硬件加速功能
  • 利用 AI 在 iPhone 上实现 App 文本情绪价值评估(下)
  • 浅谈低代码平台涉及的一些技术选型
  • 【BUUCTF系列】[ACTF2020 新生赛]Exec 1
  • 用 Ubuntu 22.04 (Jammy) 的 MongoDB 源
  • Skia-如何渲染文本(上)
  • Android中页面生命周期变化
  • 多人命题系统
  • Qt 开发自动化测试框架搭建
  • 【Open3D】基础操作之三维变换
  • Nginx跨域问题与 MIME 类型错误深度排错指南:解决 MIME type of “application/octet-stream“ 报错
  • 【LeetCode刷题指南】--单值二叉树,相同的树
  • 《人形机器人的觉醒:技术革命与碳基未来》——类人关节设计:柔性驱动革命之液压人工肌肉
  • python中appium
  • 在PyCharm中将现有Gitee项目重新上传为全新项目
  • WordPress 前端显示英文,后台显示中文的设置
  • CH7216A USB Type C上的 DisplayPort 转 HDMI 2.0 转换器【CH7216A-BF】
  • JSON 对象在浏览器中顺序与后端接口返回不一致的问题
  • 基于cygwin或msmy的windows环境下的jupyterlab的C内核搭建
  • Lipschitz连续函数
  • Flutter 替换镜像源