C++【引用】——串讲

2023-05-16

【引用】——串讲(视频89-94)

Note:
i.视频为黑马程序员C++视频,系列文章为视频听课笔记;
ii.引用不仅包含定义及简单应用,在类与对象…中也有涉及;
iii.难度指数:+++
iv.不论变量、函数名、标识符形式怎样复杂,只要我们考虑编程的本质是对内存的操作,对内存进行分析,一切逻辑都会变得清晰。

1.引用的基本语法
1)含义:给变量起别名
2)code格式:数据类型 &别名 = 变量名
例如:int &b = a,则b为变量a的引用,也是对引用b的初始化
3)内存图解:
定义引用,则建立内存关系
由图可见,若定义b为变量a的引用,则b可对变量a中存储的值进行操作。

2.引用的注意事项
1)引用定义则必须进行初始化
2)引用在第一次初始化后不可更改

3.引用做函数参数(重点)
之前提到某封装函数被调用,传参时的传递方式分为按值传递或按地址传递,前者形参的改变不能改变实参的值,后者形参的改变可以改变实参的值;或者说前者形参不能修饰实参,后者形参可以修饰实参。

实际上,按地址传递实际上是传递的是指针,其记录实参的地址,因此可以实现对实参的修饰。引入“引用”的概念之后,可利用其作为函数参数实现对实参的修饰。

至此,函数被调用时,参数的传递方式有以下几种:
1)值传递
2)地址传递
3)引用传递
举例:以完成实参值的交换为例
1)值传递

#include<iostream>
#include<string>
using namespace std;
//定义值传递交换函数
void swap01(int a,int b)
{
	//值传递交换函数无法实现实参的交换
	int temp = a;
	a = b;
	tmep = b;
	cout << "值传递形参a=" << a << endl;//a=20
	cout << "值传递形参b=" << b << endl;//b=10
}
int main()
{
	int a = 10;
	int b = 20;
	swap01(a, b);
	cout << "值传递交换后实参a=" << a << endl;//a=10
	cout << "值传递交换后实参b=" << b << endl;//b=20
}

依靠值传递交换函数,无法实现实参的交换。

2)地址传递

#include<iostream>
#include<string>
using namespace std;
//定义地址传递交换函数
void swap02(int *a,int *b)
{
	//地址传递交换函数可以实现实参的交换
	int temp = *a;
	*a = *b;
	tmep = *b;
	cout << "地址传递形参a=" << a << endl;//a=20
	cout << "地址传递形参b=" << b << endl;//b=10
}
int main()
{
	int a = 10;
	int b = 20;
	swap02(&a, &b);
	cout << "地址传递交换后实参a=" << a << endl;//a=20
	cout << "地址传递交换后实参b=" << b << endl;//b=10
}

依赖地址传递交换函数,可以实现实参的交换。

3)引用传递

#include<iostream>
#include<string>
using namespace std;
//定义引用传递交换函数
void swap03(int &c,int &d)//定义形参c为a的引用,d为b的引用
{
	//引用传递交换函数可以实现实参的交换
	int temp = c;
	c = d;
	tmep = d;
	cout << "引用传递形参a=" << c << endl;//a=20
	cout << "引用传递形参b=" << d<< endl;//b=10
}
int main()
{
	int a = 10;
	int b = 20;
	swap03(a, b);//注意这里的实参,不需要加任何东西符号
	cout << "引用传递交换后实参a=" << a << endl;//a=20
	cout << "引用传递交换后实参b=" << b << endl;//b=10
}

依赖引用传递交换函数,可以实现实参的交换,形参作了实参的引用。

【总结】:
1)封装函数在没有返回值的情况下,要想实现对main函数中实参的操作,必须在封装函数中对其内存进行操作。
2)引用类似于指针的简化版本,它不像指针一样类似于一种数据类型,需要定义,有固定的含义,即存储的是内存地址。它更像是变量的分身,代替变量完成不能完成的对内存的操作。

4.引用做函数返回值(重点、难点)
Note:
1)不能返回局部变量的引用,但可以返回静态变量的引用。
【碎碎念】:总之,所有的注意事项都跟内存息息相关。

代码示例:

#include<iostream>
#include<string>
using namespace std;

int& test01()//注意这里定义的函数返回值类型为引用
{
	int a = 10;//a为局部变量,存储在栈区,函数调用完之后,变量内存就被释放。
	return a;
}
int& test02()
{
	static int a = 100;//a为静态变量,存储在全局区,程序全部执行完毕后释放
	return a;
}
int main()
{
	//创建局部变量引用
	int &ref = test01();//ref为局部变量a的引用,但是由于a在函数执行完后即被释放,因此ref并没有可以操纵的内存
	cout << "作局部变量引用ref的值=" << ref << endl;//10,之所以输出10,是由于编译器对a保留一次的操作
	cout << "作局部变量引用ref的值=" << ref << endl;//乱码
	//创建静态变量引用	
	int &ref2 = test02();
	cout << "作静态变量引用ref的值="<< ref2 << endl;//100
	cout << "作静态变量引用ref的值="<< ref2 << endl;//100
}

2)函数的调用可以作为左值

代码示例:

#include<iostream>
#include<string>
using namespace std;

int& test02()
{
	static int a = 100;//a为静态变量,存储在全局区,程序全部执行完毕后释放
	return a;
}
int main()
{
	//创建静态变量引用	
	int &ref2 = test02();
	cout << "ref2="<< ref2 << endl;//100
	cout << "ref2="<< ref2 << endl;//100
	//函数的调用作为左值
	test02() = 1000;//test02()可以看成a的引用
	cout << "ref2="<< ref2 << endl;//1000
	cout << "ref2="< ref2 << endl;//1000
}

从上面的例子中可以看到,函数test02()作为左值可以实现对静态变量的赋值操作。

5.引用的本质

1)定义:引用的本质是一个指针常量。
涉及到指针常量:指针的指向不可更改,但指向的内存中的值可更改。
易混淆的常量指针:指针的指向可以更改,但指向的值不可更改。
2)前面在引用的注意事项中提到,引用一旦初始化就不能更改,这也是跟引用的本质有关系,引用初始化时就确定了其指向,而其指向是不可更改的。
3)之所以引用的定义形式等跟指针不同,是因为在C++中,编译器对引用进行了包装,代码示例如下:

#include<iostream>
#include<string>
using namespace std;


void func(int &ref)//等价于int * const ref = &a
{
	//等价于*ref = 20
	ref = 100;
}
int main()
{
	int a = 10;
	int &ref = a;//等价于int * const ref = &a
	ref = 20;//ref为引用,内部编译器转换为*ref = 20
	cout << "ref:" << ref << endl;//20
	cout << "a:" << a << endl;//20

	func(a);
	cout << "ref:" << ref << endl;//100
	cout << "a:" << a << endl;//100
	return 0;
}

6.引用在类与对象中的应用(拓展、难点)

1)背景:创建类person,已知其中包含age属性以及personaddperson成员函数,该成员函数可以实现两个实例对象age属性的求和。
2)要求:创建实例对象p1p2,实现p2.age与若干个p1.age的连续相加。
3.1)示例介绍:

如代码示例一所示,成员函数personaddperson的函数类型为person,从函数的输出结果中可以看到,每次调用成员函数时,this指针都指向不同的实例对象(从输出的地址可以看出),因此,代码示例一不能实现p2.age与若干个p1.age的连续相加,仅且只能实现一次相加,下一次调用时,this指针即指向由成员函数创建的新的实例对象(非p2)。

//代码示例一
class person 
{
public:
	//有参构造函数
	person(int age)
	{
		this->age = age;
	}
	person personaddperson(person &p)//返回为“类”
	{
		this->age += p.age;
		cout << "this指针" << this << endl;//输出每次成员函数调用时,this指向的对象地址
		cout << "this指向的年龄为:" << this->age << endl;//输出每次成员函数调用时,this指向对象的年龄
		return *this;
	}
	int age;
};

void test01()
{
	person p1(10);
	person p2(10);
	p2.personaddperson(p1).personaddperson(p1).personaddperson(p1);
	cout << "p2对象年龄为:" << p2.age << endl;
}

int main()
{
	test01();
}

当成员函数返回为类,输出结果为:
在这里插入图片描述

3.2)示例介绍

如代码示例二所示,成员函数personaddperson的函数类型为person&,从函数的输出结果中可以看到,每次调用成员函数时,this指针均指向实例对象p2(从输出的地址可以看出)。这可以理解为:当利用类的引用作为函数返回类型时,所有创建的示例对象都是p2的别名,因此实现了对p2内存的连续操作。因此,示例二可以实现p2.age与若干个p1.age的连续相加。return *this 实际上返回的就是this指针指向的实例对象。

//代码示例2
	person& personaddperson(person &p)//返回为“类”
	{
		this->age += p.age;
		cout << "this指针" << this << endl;//输出每次成员函数调用时,this指向的对象地址
		cout << "this指向的年龄为:" << this->age << endl;//输出每次成员函数调用时,this指向对象的年龄
		return *this;
	}

当成员函数返回为“类”的引用(代码示例如上),输出结果为:
在这里插入图片描述
【一点思考】:
1)在刚开始学习引用时,把引用单纯的勉强理解为变量的别名,当接触了引用在“类与对象”中的应用后,发现,引用可以是任何事物的别名,可以是类的别名,好像也可以是函数的别名等等。在本文6)的案例中,其作为p2实例对象的别名实现了对p2中age属性的连续操作。
2)之前我将指针可以看作一种数据类型,记为int/char... *,即可套用其他数据类型定义的模板。
同理,引用也可以看作一种数据类型,记为int/char/person &,这样可以帮助我们更好的理解代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++【引用】——串讲 的相关文章

  • ROS入门学习三——launch文件

    launch作用 xff1a 便于一次启动多个节点 xff0c 可启动本地节点和远程节点及修改添加参数服务器参数 lt launch gt lt launch gt 是所有launch文件的根标签 lt node gt node为子集标签
  • ssh连接不上 ping不通 ssh连接虚拟机连不上 远程连接不上可能出现的问题

    如果检查过ssh服务启动 防火墙也关闭了 sshd也安装了 但是怎么ping都ping不通虚拟机网关ip 但是本机可以ping通的话 检查是否虚拟机端 wifi 和 你得xshell 或者finshell 是否在同一个局域网 同一个wifi
  • Shell脚本

    文章目录 第 1 章 shell基础1 1 运维 amp shell 了解 1 1 1 运维基础1 1 2 shell简介 1 2 shell脚本 记忆 1 2 1 创建脚本1 2 2 脚本使用 1 3 变量 应用 1 3 1 什么是变量1
  • Hive基本DDL操作

    1 数据库的DDL操作 xff08 1 xff09 创建数据库 数据库在HDFS上的默认存储路径是 user hive warehouse 数据库名 db hive gt create database db hive 两者等价hive g
  • Windows和Linux双系统时间不对的问题。

    解决Windows与Ubuntu双系统时间同步问题 2016年07月11日 21 05 30 阅读数 xff1a 33115 1 问题发现 本子上装的是Window 10 win7升级 和Ubuntu GNOME 14 04的双系统 一直以
  • stm32移植freertos报错keil

    Using Compiler 39 V5 06 update 7 build 960 39 folder 39 d Keil v5 ARM ARMCC Bin 39 compiling main c USER FreeRTOSConfig
  • ARMv8 (arrch64)pytorch深度学习环境搭建

    Aarch64 安装Anaconda 和 pytorch 一 安装miniconda 参考链接 xff1a 82条消息 Jetson Nano xff08 aarch64 xff09 搭建miniconda 和mmdetection环境 瑾
  • GPU 选择 深度学习 图像识别

    GPU 选择 深度学习 图像识别 1 显卡1 1 Nvidia显卡分类1 1 1 Geforce系列1 1 2 Quadro系列1 1 3 Tesla系列1 2 GPU几个比较重要的参数GPU架构 xff1a CUDA核心数量 xff1a
  • git 合并两个分支

    这里讲解具体的思想 xff0c 具体的提交命令百度上有 目前的场景是将两个非master上的分支进行合并 xff0c 并且不造成冲突 比如现在有两个分支a xff0c b 现在将b合并到a 首先及将b分支上的代码clone下来 xff0c
  • ubuntu中解决ROS--Gazebo添加模型库,解决打开后无模型的问题

    ubuntu中解决Gazebo添加模型库 xff0c 解决打开后无模型的问题 1 在主目录中ctrl 43 h 下载https bitbucket org osrf gazebo models downloads ExBot ROS专区 x
  • 大小端模式

    定义 xff1a 数据存放在地址中 xff0c 一个地址码对应一个字节 内存的低地址存放数据的低位 xff0c 则存储方式为小端存储 xff1b 内存的低地址存放数据的高位 xff0c 则为大端存储 判断大小端的方法有两种 xff1a 数据
  • Matlab二维插值

    Matlab二维插值 interp2函数 xff08 网格节点 xff09 Y 61 interp2 x y z xi yi method method nearest 最邻近插值linear xff08 默认 xff09 双线性插值cub
  • 4、ROS话题通信实战,代码逐行解析

    在前面小节已经实现了ROS工作空间的创建 xff0c 这里开始功能包的开发实战了 首先创建功能包 xff1a cd ROS src 创建功能包topic xff0c 指定依赖roscpp和std msgs包 catkin create pk
  • 大数据学习(三十)JOIN过程中的mapreduce阶段

    前言 xff1a join分为mapjoin 和 common 普通 join mapjoin 是没有reduce阶段 只有map阶段 在map阶段进行join操作 xff08 此知识点也会在大表join小表中体现 xff09 common
  • Gunicorn介绍、安装及使用

    1 简介 Gunicorn Green Unicorn 是一个 UNIX 下的 WSGI HTTP 服务器 xff0c 它是一个 移植自 Ruby 的 Unicorn 项目的 pre fork worker 模型 它既支持 eventlet
  • Spring Boot POM 详解

    正如这幅图所展示的那样 xff0c 在Spring IO Framework体系中 xff0c Spring Boot处在Execution layer xff0c 来看看官方对这层的解释 xff1a The Spring IO Execu
  • 基于阿木实验室P200飞行器simulink模型开发的多旋翼无人机自抗扰控制器(ADRC)参数调整和仿真

    1 飞行器模型参数 参考P200飞行器参数 利用网站https www flyeval com 计算飞行器如下 xff1a 2 参数设置具体如下 1 xff09 模型主要包括总线 期望数据生成模块 控制器 PWM生成器 飞行器对象模型 2
  • mkdir()函数

    一 创建目录 1 1 direct h 头文件 int mkdir const char path mode t mode 函数名 mkdir 功 能 建立一个目录 用 法 int mkdir const char dirname 头文件库
  • react路由基础与传参

    react router dom 1 react的一个插件 xff0c 专门用来实现单页面 2 基于react的项目基本都会用到它 路由的基本使用 1 路由导航区标签为Link或NavLink标签 lt Link to 61 34 xxx
  • ROS TF工具的使用

    1 打印坐标系转换关系 tf echo 命令格式 xff1a rosrun tf tf echo lt source frame gt lt target frame gt 输出数据类型 xff1a geometry msgs Transf

随机推荐