《C++程序设计》阅读笔记【5-引用】

本文详细介绍了C++中的引用概念,包括引用的声明、初始化、操作规则,以及如何在函数参数传递和返回值中使用。讨论了const限定引用的特性以及与指针的区别,还涉及了对堆中变量返回引用的注意事项。
摘要由CSDN通过智能技术生成

在这里插入图片描述

在这里插入图片描述

🌈个人主页:godspeed_lucip
🔥 系列专栏:《C++程序设计》阅读笔记

本文对应的PDF源文件请关注微信公众号程序员刘同学,回复C++程序设计获取下载链接。



1 引用

1.1 概念

引用作为目标的别名而使用,对引用的改动实际就是对目标的改动。

例如:rInt就是someInt的引用

int someInt;
int  &rInt = someInt;
//int &rInt、int& rInt、int & rInt都是等价的

引用不是值,不占存储空间,声明引用时,目标的存储状态不会改变。

引用只有声明,没有定义(定义必然设计到分配具体空间)

引用在声明时必须被初始化,则会产生编译错误。例如:int &rInt是错误的

虽然引用运算符与地址操作符使用相同的符号(&),但它们不是一样的

&除了引用之外,其他任何时候使用都表示取地址操作符。例如int i=1; cout<<&i;是输出整形变量i的地址。

1.2 和引用相关的操作

引用的地址是被引用目标的地址

引用被建立后,实际上就不能让引用指向新的引用目标,因为对引用的操作就是对其引用目标的操作。请看下面的例子:

#include<iostream>
using namespace std;

int main(){
	int origin = 1;
	int & r_origin = origin; //建立对origin变量的引用
	cout<<&origin<<endl;
	cout<<&r_origin<<endl; //输出两者的地址
	
	cout<<origin<<endl;
	cout<<r_origin<<endl; //输出两者的值
	
	int other = 2; //新建立一个int变量
	r_origin = other; //试图让引用指向新的引用目标
	cout<<origin<<endl;
	cout<<r_origin<<endl;
	cout<<other<<endl;//输出三者的值
    exit(0);
}

结果:

image-20240229135316766

从上面这个例子也可以看出引用和指针的差别:

引用不能改变指向,但是指针是可以改变指向的(指针常量除外)

1.2.1 什么能被引用

  1. 若一个引用对象被声明为Type &r = value,则要求valueType类型,或者value可以被隐式转换为Type类型。

有待商榷,我的C++03版本的dev中,下面的代码是错误的:

int main(){
	int a = 1;
	double &b = a;
 return 0;
}
  1. 如果引用类型Type的初始值不是一个左值,那么将建立一个Type类型的目标并用初始值初始化,那个目标的地址变成引用的值。

事实上我感觉这条有待商榷。我的C++版本是C++03。下列代码都是错误的:

void test02() {
	double& r_dou_1 = 1;
	double& r_dou_2 = 1.0;
}

只有这样才是正确的:

void test02() {
	double sour = 1.0;
 double& r_dou_2 = sour;
}
  1. 指针也是一种数据类型,也可以被引用。例如:
void test03(){
	int b=1;
	int* a = &b;
	int* &point_r = a;
	cout<<*point_r<<endl;
}

结果:

image-20240229143646930

TIP:
int* a = &1;是错误的,只能像int b=1; int* a = &b;才可以。

因为1是一个常量右值,它没有一个明确的内存地址

  1. 不可以对void进行引用。例如
void& a=3; //error

void只是在语法上相当于一个类型,本质上不是类型,没有任何一个变量或对象的其类型为void

  1. 数组不能被引用

一方面,数组是某个数据类型元素的集合,每个元素皆为引用,意味着每个元素必须初始化为其他内存实体;并且数组的大小必须在编译时就确定,但是引用是在运行时才进行绑定的,因此引用的数组是错误的;

另一方面,数组名只是表示该元素集合空间的起始地址,若对其引用,那就是数组的别名,与指向数组的指针没有什么区别

其实好像也是可以引用的,例如:

int main(){
 int arr[] = {1, 2, 3, 4, 5};
 int (&ref)[5] = arr; // 引用数组
 cout<<ref<<endl;
 return 0;
}

其结果就是:

image-20240321124812610

  1. 不可以对引用再次引用,也不可以用指针指向引用

其实很好理解,引用本质上不是对象,不占用内存空间。被引用和指向的目标都必须是一个对象。

  1. 引用不可以使用类型来初始化

例如:

int &ra = int; //error

理解:引用是变量或者对象的引用,而不是类型的引用

  1. 空引用不能存在,如:int &r = NULL是错误的

1.3 用引用传递函数参数

因为引用就是变量的别名,传递引用就是在传递引用对象本身。

传递引用给函数与传递指针的效果一样,传递的是原来的变量或对象(事实上,在底层还是通过传递地址的方式实现的),而不是在函数作用域内建立变量或对象的副本。

例子:使用引用交换变量值

void swap(int &x,int &y){
	int temp = x;
	x = y;
	y = temp;
}

void run_swap(){
	int a=0;
	int b=4;
	cout<<a<<endl;
	cout<<b<<endl;
	swap(a,b);
	cout<<a<<endl;
	cout<<b<<endl;
}

int main(){
	run_swap();
}

结果:

image-20240229151853914

注意:下面的两个重载函数会报错:

image-20240229152116576

1.4 用引用返回值

请看以下例子:

image-20240229152917992

对于第一种情况:

fn1以值的形式返回。在返回全局变量temp时,编译器会在fn1函数的栈空间中,创建临时变量存储temp的值。之后再将该临时变量赋值给a

对于第二种情况:

fn1以值的形式返回。与第一种情况相同,编译器同样会创建临时变量。但是接受该变量的是一个引用,于是引用b的引用变量就变成了该临时变量。但是,由于临时变量是存储在fn1的栈空间中的,但是当fn1返回后,其数据就会被销毁,所以引用b就会指向一个无意义的数据。因此会出现warning。正确做法是这样的:

int  x = fn1(5.0);
int &b = x;

对于第三种情况:

fn2以引用的形式返回,因此不会创建数据副本,而是直接把c赋值为全局变量temp的值。

对于第四种情况:

以引用的形式返回,同时以引用的形式接受。这时候,引用d的目标对象就是temp

返回局部变量的引用是错误的(类似于返回局部变量的指针)。因为这可能导致对已释放内存的引用或失效的对象的引用,进而引发未定义行为。

1.5 const限定引用

简单来说,加上const之后,引用就只能被读,不能被修改

例如下面的代码:
image-20240229161955603

其结果为:(代码较简单,不详细解释)
image-20240229162013013

1.6 返回堆中变量的引用

小tip:

正确代码:

int *p =  new int(20);
int* &ref = p;

错误代码:

int* &ref = new int(20);

解释:原因是 new int(20) 返回的是一个右值(rvalue),而非左值(lvalue)。在C++中,非const引用无法绑定到右值,因为右值通常是临时的,不可修改。

考虑到new不一定能成功申请到内存,所以可能返回NULL,但是引用不可以是NULL。所以最好增加一个检查

释放内存:

在上述的例子中,ref是指针p的引用,因此可以使用delete refdelete p释放。

当然,假如代码是这样的:

int *p =  new int(20);
int &ref = *p;

那么,使用delete &ref(注意这里的&是取地址操作)或者delete p释放。

2 总结

C++,犹如编程的交响乐, 在代码的海洋中奏响和谐的旋律。

它是创造者的笔,雕刻着无尽可能,

是思想的翅膀,让梦想飞翔的天空。

无拘无束,灵活多变。

C++,是程序员心中的宝藏,永不凋零的花朵。

渴望挑战C++的学习路径和掌握进阶技术?不妨点击下方链接,一同探讨更多C++的奇迹吧。我们推出了引领趋势的💻C++专栏:《C++程序设计》阅读笔记,旨在深度探索C++的实际应用和创新。🌐🔍

在这里插入图片描述

在这里插入图片描述

  • 36
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值