【C++初阶】:C++入门,引用概念及其性质

news2024/11/17 1:47:57

文章目录

      • 一、引用的概念
      • 二、引用的语法规则
        • 1、引用特性
        • 2、常引用
      • 二、引用的使用场景
        • 1. 引用做参数
        • 2. 引用做返回值
      • 三、引用和指针的区别

在这里插入图片描述

一、引用的概念

首先明确一下,引用不是定义一个新的变量,而是给已经存在的变量起一个别名,变量和他的引用是共用同一块内存空间。

C++中的引用就是和人的外号一样,人的本名和外号都是指同一个人。比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

二、引用的语法规则

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

#include<iostream>
using namespace std;

int main()
{
	int a = 10;
	int& b = a;  // b是a的引用
	cout << a << endl;
	cout << b << endl;
	return 0;
}

在这里插入图片描述
变量a 和它的引用b使用的是同一块内存空间

int main()
{
	int a = 10;
	int& b = a;
	cout << &a << endl;
	cout << &b << endl;  // a 和 b 使用的是同一块内存空间
	return 0;
}

在这里插入图片描述

int main()
{
	int a = 10;
	int& b = a;
	b = 20; 
	cout << a << " " << b << endl;
	return 0;
}

在这里插入图片描述由于变量和变量的引用是使用的同一块内存地址,这意味着如果改变b,那么a也会随着b的改变而改变。

1、引用特性

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

void testref()
{
	int a = 10;
	int& b;  // err 引用是必须初始化
}

在这里插入图片描述

2、一个变量可以有多个引用
一个变量被引用后,可以继续被引用,也可以引用变量的引用。

void testref02()
{
	int a = 10;
	int& b = a;
	int& c = a;  // 一个变量可以有多个引用 
	int& d = b;  // 可以引用变量的引用
}

3、引用一旦引用一个实体(变量),就不能在引用其他实体

void testref02()
{
	int a = 10;
	int b = 20;
	int& c = a;
	int& c = b;  // c不可以重复引用
}

在这里插入图片描述
这里要和C/C++中的指针区分一下,指针可以改变指针指向的对象,但是引用不可以。

2、常引用

一般来说,引用只可以引用变量,不可以直接引用常量的。因为常量是不可以改变的。

void testref03()
{
	int& a = 10;  // err, 不可以引用常量
}

在这里插入图片描述
解决办法
想要引用常量的话,只要在引用的前面加一句const,加上const之后,引用就不可以发生改变。
除了上述情况,引用的类型不同也会导致编译不同过,这是因为在发生类型转化的过程中,会产生临时变量,而临时变量具有常性,和常量一样,需要用const引用。

在这里插入图片描述

void testref04()
{
	char ch = 'a';
	int& a = ch;  // 不可以引用不同类型的变量,类型转化时会产生临时变量,临时变量具有常性
}

在这里插入图片描述

产生临时变量时都要用常引用。

void testref05()
{
	char ch = 'a';
	const int& a = ch;
}

二、引用的使用场景

1. 引用做参数

在C语言学习指针的时候一定学过一个交换函数Swap,那时候我们在Swap时参数必须要用传址调用,因为如果用传值调用的话,形参的改变是不影响实参的,无法实现交换功能。
在这里插入图片描述

但是如果我们学了引用的话就不需要使用传址调用了,可以直接用引用作为参数,引用的形参就是实参的别名

void func(int& x, int& y)
{
	cout << &x << endl;
	cout << &y << endl;
}

int main()
{
	int a = 10;
	int b = 20;
	cout << &a << endl;
	cout << &b << endl;
	cout << "-------------" << endl;
	func(a, b);
	return 0;
}

在这里插入图片描述
用引用的话,在函数传参时就不需要传址调用了,更加方便。

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
2. 引用做返回值

是的,你没看错,引用不仅可以做函数的参数,还可以做函数的返回值,我们之前写的具有返回值的函数返回的并不是直接将函数中的变量,而是返回的变量的一份临时拷贝,而拷贝是需要付出一定代价的,这样会降低我们代码的效率,但是如果我们用引用返回的话就可以避免拷贝的代价,在对程序性能要求高且允许使用引用的情况下,尽量使用引用返回。

注意:并不是所有的函数都可以用引用返回,有些强行使用引用返回会造成一些不必要的错误。
函数可以用引用返回的条件:
函数的返回值在出函数作用域后不会被销毁。

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

静态变量出了函数定义域之后不会销毁,可以用引用返回。

int& Count()
{
	int n = 0;
	n++;
	// ...
	return n;  // n在出函数作用域后就销毁了,不要用引用返回
}

传值、传引用的的效率比较
用值作为参数或返回值时,在传参和返回期间,函数是不会直接传递实参或则是将变量之间返回,而是传递实参或者返回变量的一份临时拷贝,所以用值传参或者做返回值效率是非常低的,尤其是当参数或者返回值类型非常大时,效率更加低下。

值和引用作为函数参数的效率对比:

#include <time.h>
// 传值
struct A{ int a[10000]; }; 
void TestFunc1(A a) {}
// 传引用
void TestFunc2(A& a) {}

void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; i++)
	{
		TestFunc1(a);
	}
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; i++)
	{
		TestFunc2(a);
	}
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

在这里插入图片描述
传值的效率明显低于传引用。

值和引用作为函数返回值的效率对比:

#include<time.h>
struct A { int a[10000]; };

A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }

void TestReturnByRefOrValue()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (int i = 0; i < 100000; i++)
		TestFunc1();
	size_t end1 = clock();
	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (int i = 0; i < 100000; i++)
		TestFunc2();
	size_t end2 = clock();
	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1() Time:" << begin1 - end1 << endl;
	cout << "TestFunc2() Time:" << begin2 - end2 << endl;
}

int main()
{
	TestReturnByRefOrValue();
	return 0;
}

在这里插入图片描述
传值和指针在作为传参以及返回值类型上效率相差很大。

三、引用和指针的区别

1、语义概念上引用就是变量的别名,没有独立空间,和引用实体共用同一块空间。

int main()
{
	int a = 10;
	int& b = a;
	cout << &a << endl;
	cout << &b << endl;
	return 0;
}

在这里插入图片描述
底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

在这里插入图片描述
2、引用必须初始化,指针可以在定义时不初始化

int a = 20;
int* pa;  // 指针可以不初始化
int& ra;  // 引用必须初始化

在这里插入图片描述

3、 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

4、没有NULL引用,但有NULL指针。

int* pa = NULL;
int& ra = NULL;  // err,没有NULL引用

在这里插入图片描述
5、在sizeof中含义不同引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)

#include<string>
int main()
{
	string str = "abcdef";
	string& rstr = str;
	string* pstr = &str;
	cout << "string& rstr = str: " << sizeof(rstr) << endl;
	cout << "string* pstr = &str: " << sizeof(pstr) << endl;
	return 0;
}

在这里插入图片描述
6、 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

int main()
{
	int a = 10;
	int& ra = a;
	int* pa = &a;
	ra++;
	pa++;
	cout << "int& ra = a:  " << ra << endl;
	cout << "int* pa = &a: " << pa << endl;
	return 0;
}

在这里插入图片描述
7、有多级指针,但是没有多级引用

int a = 10;
int* pa = &a;
int** ppa = &pa;
int& ra = a;
int&& rra = ra;  // 无多级引用

在这里插入图片描述
8、访问实体方式不同,指针需要显式解引用,引用编译器自己处理

int main()
{
	int a = 10;
	int* pa = &a;
	int& ra = a;
	cout << ra << endl;
	cout << *pa << endl;
	return 0;
}

在这里插入图片描述
9、引用比指针使用起来相对更安全,毕竟指针存在野指针,而不存在野引用。

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

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

相关文章

相机内存卡格式化了照片怎么恢复?格式化恢复详解

摄影爱好者们都知道&#xff0c;相机内存卡是记录我们美好瞬间的重要媒介。然而&#xff0c;在使用过程中&#xff0c;有时我们会因操作不当或设备故障&#xff0c;不小心格式化了内存卡&#xff0c;从而导致珍贵的照片丢失。面对这种情况&#xff0c;我们该如何恢复这些被格式…

贪吃蛇项目实现(C语言)——附源码

前言 贪吃蛇是一款十分经典的游戏&#xff0c;其通过控制贪吃蛇的上下左右移动来吃食物&#xff0c;延长自己的身体&#xff0c;也会因为撞到墙体和自身而死亡。下面我们通过C语言来实现贪吃蛇。 1.技术要点 C语言枚举&#xff0c;结构体&#xff0c;链表&#xff0c;动态内…

内网中的RDP利用

学习参考 https://www.freebuf.com/articles/network/276242.html能跟着实操的都实操一下。熟悉一些命令&#xff0c;过程。 实验环境&#xff1a;win2008&#xff0c;192.168.72.139 两个用户&#xff1a; administrator&#xff0c;shizuru RDP服务 确定/开启 RDP服务确…

力扣第79题 单词搜索

前言 记录一下刷题历程 力扣第79题 单词搜索 单词搜索 原题目&#xff1a;给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻…

解决ubuntu安装modelsim20.1 32位库依赖失败问题(附简易安装方法)

先说方法&#xff1a;不用管&#xff0c;直接继续安装软件就行。 注意&#xff0c;选中 libgtk-3-0t64:i386 而非 libgtk-3-0:i386 注意&#xff0c;选中 libcanberra0t64:i386 而非 libcanberra0:i386 注意&#xff0c;选中 libpng16-16t64:i386 而非 libpng16-16:i386 注意&…

白盒测试_学习总结

目录 一、白盒测试的概念理解 二、白盒测试的分类 1、静态分析 2、动态分析 &#xff08;1&#xff09;逻辑覆盖测试 a、语句覆盖 b、判定覆盖 c、条件覆盖 d、判定条件覆盖 e、条件组合覆盖 f、路径覆盖 &#xff08;2&#xff09;基本路径测试法 3、总结 一、白盒…

Arduino基础入门学习——BH1750(GY-302)+ LED模拟自动路灯的实现

BH1750&#xff08;GY-302&#xff09; LED 一、前言三、准备工作四、程序代码五、运行结果六、结束语 一、前言 相信很多人都见过一种路灯&#xff0c;白天的时候它是不亮的&#xff0c;等到了晚上环境变暗时就开始它的照明工作了&#xff0c;不了解的人可能认为是人为操纵它工…

如何编写Prompt,利用AI高效生成图表——图表狐(FoxChart)指南

在数据可视化领域&#xff0c;图表是数据的重要表达方式。为了让更多人能够轻松高校地生成美观、专业的图表&#xff0c;图表狐(FoxChart)应用而生。然而&#xff0c;要想充分发挥AI的潜力&#xff0c;编写合适的Prompt至关重要。本文介绍一些编写Prompt的原则&#xff0c;帮助…

代码随想录算法训练营第39天|198.打家劫舍、 213.打家劫舍II、337. 打家劫舍 III

目录 198.打家劫舍1、题目描述2、思路3、code4、复杂度分析 213.打家劫舍II1、题目描述2、思路3、code4、复杂度分析 337. 打家劫舍 III1、题目描述2、思路3、code4、复杂度分析 198.打家劫舍 题目链接&#xff1a;添加链接描述 1、题目描述 你是一个专业的小偷&#xff0c;计…

8、用户行为数据同步

1、 数据通道 用户行为数据由Flume从Kafka直接同步到HDFS&#xff0c;由于离线数仓采用Hive的分区表按天统计&#xff0c;所以目标路径要包含一层日期。具体数据流向如下图所示。 2、 日志消费Flume配置概述 按照规划&#xff0c;该Flume需将Kafka中topic_log的数据发往HDFS。…

cmake编译MQTT-C源码

Windows端编译MQTT-C源码&#xff0c;获取mqttc库&#xff08;动态库与静态库&#xff09;&#xff0c;用于集成到Qt工程中使用mqtt订阅与发布功能。 编译源码与编译出来的mqttc动态库、静态库下载​​​​​​​​​​​​​​https://download.csdn.net/download/qq_38159549…

直播怎么录屏?录屏网页的工具有吗?推荐这3款你千万不要错过~

直播与网页录屏&#xff1a;三款必备录屏软件推荐 为什么要记录直播&#xff1f;直播可以捕捉实时发生事件&#xff0c;是真真实实的one take&#xff0c;更重要的是可以记录直播画面中的实时弹幕、评论区的互动&#xff0c;无论是激动人心的体育赛事、教育课程还是互动性强的连…

C++11 14 17 20 23进化史

C11、C14、C17、C20和C23是C语言标准的不同版本&#xff0c;它们之间在功能、特性和语法上存在一些区别。以下是对这些版本主要区别的概述&#xff1a; C11 C11是C语言的一个重要标准&#xff0c;引入了大量新特性和改进&#xff0c;使C变得更加易用和强大。主要特性包括&…

Qt工程使用MQTT-C库与mqtt服务器数据通信

实现mqtt订阅与发布话题&#xff0c;与mqtt服务器进行数据通信 编译环境&#xff1a;Qt5.15.2 vs2019 需要mqttc库&#xff1a;mqttc.lib, mqttc.dll&#xff08;根据MQTT-C源码编译出来的库&#xff0c;参考cmake编译MQTT-C源码-CSDN博客&#xff09; 一、Qt pro文件编写 …

android kotlin 基础复习 继承 inherit

1、新建文件kt 2、代码&#xff1a; /**用户基类**/ open class Person1(name:String){/**次级构造函数**/constructor(name:String,age:Int):this(name){//初始化println("-------基类次级构造函数---------")println("name:${name},age:${age}")} }/**子…

信息安全工程师(1)计算机网络分类

一、按分布范围分类 广域网&#xff08;WAN&#xff09;&#xff1a; 定义&#xff1a;广域网的任务是提供长距离通信&#xff0c;运送主机所发送的数据。其覆盖范围通常是直径为几十千米到几千千米的区域&#xff0c;因此也被称为远程网。特点&#xff1a;连接广域网的各个结点…

计算机毕业设计 财会信息管理系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【吊打面试官系列-Redis面试题】怎么理解 Redis 事务?

大家好&#xff0c;我是锋哥。今天分享关于【怎么理解 Redis 事务&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 怎么理解 Redis 事务&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 1&#xff09;事务是一个单独的隔离操作&#xff1…

【LabVIEW学习篇 - 22】:ActiveX

文章目录 ActiveXActiveX打开Windows颜色选择对话框ActiveX将浏览器嵌入到前面板 ActiveX ActiveX是微软推出的一个开放的技术集的统称&#xff0c;它是很早之前出现的OLE(object linking and Embedding)技术的扩展&#xff0c;它是基于COM(Component Object Model)技术而建立…

meta元素

1&#xff0c;meta元素有4个全局属性 charset 设置文档的编码类型&#xff0c;通常设置为utf-8 <meta charset"utf-8" /> content 配合name或者http-equiv属性使用&#xff0c;为其value、 name 元数据名称(name的值)说明application name当前页所属Web应用系…