C++学习-引用的那些事

回顾指针、引出引用

在c语言中,使用指针,可以间接获取、修改某个变量的值

int main(){
	int num = 10;
	int *p = #
	*p = 20;//改变指针的指向,这样就间接修改了num的值
	return 0;
}

但是在C++中,使用引用(Reference)可以起到跟指针类似的功能

int main(){
	int num = 10;
	int &ref = num;//ref引用了变量num
	ref = 20;//改变ref,就相当于改变num,也起到了间接修改num的功能
	return 0;
}

定义

引用相当于变量的别名,后续对引用的任何操作,都相当于间接的对变量进行操作,引用变量时,用‘&’符号,而指针变量指向变量是,则用‘*’符号。下方是引用的一般说明例子

永远牢记:引用就是变量的别名,引用某个变量,可以理解为‘指向’某个变量!!!!!!!!!

int main(int argc, char **argv)
{
	int age = 10;
	int *P_age = &age;//这叫定义指针变量P_age,指向age的值
	int &R_age = age;//这叫引用变量R_age,引用age的值
	R_age = 20;//改变引用的值,就相当于 age = 20 因为R_age是age的别名
	return 0}

引用的用法

1、可以利用引用初始化另一个引用,相当于某个变量的多个别名

int main(int argc, char **argv)
{
	int age = 10;
	int &R_age = age;//这叫引用变量R_age,引用age的值
	R_age = 20;//改变引用的值,就相当于 age = 20 因为R_age是age的别名

	//可以利用引用初始化另一个引用,相当于某个变量的多个别名
	int &R_age_1 = R_age;//R_age_1也相当于age的别名
	int &R_age_2 = R_age_1;//R_age_2同时也相当于age的别名
	//从此,程序中对R_age和R_age_1和R_age_2操作,都相当于对age操作,也就是变量age有了多个别名
	
	return 0}

2、通过程序实现两个数字的交换,从而体现出引用的方便之处

定义两个int型变量,int a = 10; int b = 20; 然后编写值交换函数,交换a和b的值

//值传递函数
void swap_val(int v1, int v2)
{
	int temp = v1;
	v1 = v2;
	v2 = temp;
}

//指针传递函数,但编写很麻烦,调用也麻烦,还要加*和&
void swap_ptr(int *v1, int *v2)
{
	int temp = *v1;
	*v1 = *v2;
	*v2 = temp;
}

//引用传递函数,编写方便,调用方便
void swap_ref(int &v1, int &v2)
{
	int temp = v1;
	v1 = v2;
	v2 = temp;
}

int main(int argc, char **argv)
{
	int a = 10;
	int b = 20;
	swap_val(a, b);
	cout << "a=" << a << "b=" << b << endl;
	//a=10 b=20 不会改变ab的值,因为我改变的是v1、v2的值,而v1、v2是局部变量,函数调用完之后自动销毁

	swap_ptr(&a, &b);
	cout << "a=" << a << "b=" << b << endl;
	//a=20 b=10 因为改变的是地址的值,所以会改变地址指向的值

	swap_ref(a, b);//进入引用函数,v1就是a的别名,改变v1就是改变a!!!恍然大悟
	cout << "a=" << a << "b=" << b << endl;//a=20 b=10 交换了ab的值,而且函数编写和调用都非常简便
	return 0}

对比上述几个交换函数,我们可以发现:引用传递的性质类似于指针传递,但是代码书写方式却类似于值传递。实际上,引用的能实现的功能,指针都可以实现,那为什么还要用‘引用’呢?(答案见第五节第一部分)

3、指针也可以被引用,叫做指向指针的引用

注意:引用某个变量时,变量是什么类型,这个引用就必须是什么类型

int main(int argc, char **argv)
{
	//指针也可以引用!!!叫做指向指针的引用,但没有指向引用的指针
	int value = 10;
	int *p = &value;
	int *&ref = p;//&ref左边是什么类型,这个引用就引用什么类型的变量,结果就是ref就是p的别名
	*ref = 30;//相当于*p=30,因为ref从上一课开始已经是指针p的别名了;即赋值30给p指向的变量,即value

	//通过引用,间接修改指针的指向
	int height1 = 20;
	ref = &height1;//相当于p=&height;即修改了指针的指向!
	return 0}

4、在定义时,必须初始化,一旦指向了某个变量,就不可以改变

int main(int argc, char **argv)
{

	int value = 10;
	int height = 20;
    int &ref = value;
	&ref = height;//会报错,因为ref引用了value之后,就不可以再引用别的变量了
	
	//引用定义时就必须初始化,所以下面的书写是不对的
	int &ref;
	&ref = value;

	return 0}

5、重新赋值指针和引用,对所指向变量的影响

对指向源变量的指针重新赋值,不会改变原变量的值,对引用源变量的引用重新赋值,会改变原变量的值(对指针重新赋值是改变指针的指向,可不是对指针指向的内存区域重新赋值)

int main(int argc, char **argv)
{

	int height = 10;
	int temp = 30;
	int *P_height = &height; //指针指向height
	int &ref_height = height; //引用指向height
	P_height = &temp;//对指针重新赋值,意思是改变指针的指向,故不会改变原变量的值
	//这里注意一个很重要的问题,我对P_height赋值&temp,也就是让P_heught指向temp,height不会改变,
	cout << height << endl;//10
	
    ref_height = temp;//对引用重新赋值,就会改变原变量的值
    cout << height << endl;//30

	return 0}

引用与指针的比较

1、怎样理解引用比指针更安全?

指针这个东西,用好了非常强大,但是用不好,造成的后果也是很严重的。指针可以指向任何内存空间,还可以对其进行赋值(这里说的是对指向的存储空间赋值),这样就很危险。万一这块存储空间存放着比较重要的数据,如果可以随意更改的话,就会出事。而引用却不同,引用只能指向一个变量,不会像指针一样可以乱指,所以对比指针比较安全。

2、一些特性的比较

  • 引用的代码书写更简单,而指针编写代码较为复杂
  • 引用在创建的同时必须初始化,即必须引用一个有效的对象;而指针在定义时不必初始化,可以在定义后面的任何地方重新赋值
  • 不存在引用=NULL,引用必须与合法的存储单元关联;而指针是可以=NULL的
  • 引用一旦指向一个对象,它就不可以改变为另一个对象的引用(江湖人称“从一而终、忠贞不渝”);而指针在任何时候都可以改变指向。(注意:给引用赋值并不是改变引用与原变量的绑定关系,仅仅会改变原变量的值,引用依然是原变量的别名
  • 本质一些的东西:在代码层面,引用的用法和对象一样;但在汇编层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换

const引用的那些事

1、定义

一旦const引用一个对象,这个对象可以访问,但不能修改,即无法间接的修改原对象了。
引用可以用const修饰,这样就无法通过引用修改数据了,被称作常引用那有什么意义呢?
一般这种const int &ref 通常会在函数的形参里出现,常引用的目的是:保证传进来的数据是只读的,即我只能读取到ref的数据,不可以对其进行修改。是有应用场合的!!!

2、const指针(非常重要)

用于牢记,const只修饰右边的部分,只要不单独修饰的部分,都可以更改!!!!
int *const p 和 int const *p的区别

int main(int argc, char **argv)
{

	int height1 = 10;
	int age1 = 20;
	int * const p1 = &height1;//const修饰p,意味着p不可以被赋值,也就意味着指针p不可以更改指向
	p1 = &age1;//报错
	
	*p1 = 20;//不报错,这是因为const并没有修饰到*p,所以*p可以被赋值,也就意味着可以间接修改height值
	cout << height1 <<endl; //20

	int height2 = 10;
	int age2 = 20;
	int const *p2 = &height2;// const修饰*p2,意味着*p2无法赋值,但p2可以被赋值,
	                         //也就意味着可以更改指向,但不能更改指向的数据
	*p2 = 20;//报错
	p2 = &age2;//不报错                       
	return 0}

3、const引用的规则

const必须写在‘&’符号前面才能算常引用

int main(int argc, char **argv)
{

	int height1 = 10;
	int age1 = 20;
	int & const ref1 = height1;//const修饰ref1,意味着ref1不可以更改指向,而引用本来就不可以更改指向
	//int &ref1 = &height1;//与上行代码效果相同
	ref1 = 20;//不会报错

	//常引用
	int const &ref2 = age1;//const修饰的是整个&ref2,所以引用变量此时不可以间接修改原变量的值
	ref2 = 30;//报错
	
	return 0}

4、const引用的用武之地

想要函数的形参既可以接收变量传入,也可以接受常量传入,这时候就要用到const引用

const引用作为函数形参时,可以接收“实参”的范围比较大,在面向对象中用处很多

//const引用作为函数形参
//这样的话,sum传常量和变量都可以了,这个非常重要!!!!
int sum(const int &v1, const int &v2)
{
	return v1 + v2;
}

int main(int argc, char **argv)
{
	int a = 10;
	int b = 20;
	//const引用作为函数形参数,实参可以是常量(const实参)也可以是变量(非const实参)
	sum(a, b);//非const实参
	sum(1, 2);//const实参
	return 0}

const引用虽然不可以通过引用间接修改原变量的值,但是可以直接通过修改原变量的值,达到修改引用的指向

int main(int argc, char **argv)
{
	//const引用虽然不可以通过引用修改所指向的值,但是依然可以修改变量达到修改所指向的值
	int value_int = 10;
	const int &ref_value_int = value_int;
	ref_value_int = 20;//会报错!!!,因为const引用不允许通过修改引用来间接修改变量值
	value_int = 20;//不管引用的事,我依然可以随意更改原变量,不会报错
	cout << "ref_value_int=" << ref_value_int << endl;//20,此时引用指向的数值被改变了
	
	return 0}

常引用const&,可以指向不同数据类型的变量。但如果此时修改原变量的值,引用指向的内容不会发生变化(为什么呢?)

int main(int argc, char **argv)
{
	int value_int = 10;
	const double &ref_value_double = value_int;//const double类型引用int型变量
	value_int = 30;//改变原变量的值
	cout << value_int << endl;//30
	cout << ref_value_double << endl;//10,即使修改了原变量的值,引用依然指向初始值10
	return 0}

因为:当const引用指向了不用数据类型的变量时,会产生一个匿名的临时变量,所以上面的代码有一些隐藏代码

int main(int argc, char **argv)
{
	int value_int = 10;
	int temp = value_int ;//temp = 10,这句是隐藏代码,编译器自动生成的
	const double &ref_value_double = temp;//
	value_int = 30;//即使改变原变量的值,也不会改变引用的指向
	cout << value_int << endl;//30
	cout << ref_value_double << endl;//10,即使修改了原变量的值,引用依然指向初始值10
	return 0}

补充的小tips

1、如果查看引用所占字节数

我们通常的想法就是直接用sizeof(引用)来判断,但这是不准确的,这种问题,很考验本质的。首先我们要知道一件事,就是CPU架构在x86和x64环境下,指针存储字节是不一样的。在x86,指针占4个字节,在x64,指针占8个字节。(注意:引用和指针所占字节数是相同的)

struct test
{
 	int &ref;
};
int main(int argc, char **argv)
{

	int height = 10;
	int &ref_height = height; //引用指向height
	cout << sizeof(ref_height) <<endl; //我们通常会这么干,但这是不准确的
	//为什么不能这么测试引用所占字节数呢?
	//因为sizeof(ref_height)里面的ref_height其实就是height,所得到的结果其实就是height的所占字节数
	//也就是int类型所占的字节数,也就是4

	//那如何,合理的测试引用所占用的字节数呢?可以定义一个结构体,结构体内只存放一个引用变量
	//然后,可以通过sizeof结构体,来侧面证明一下引用和指针所占字节数是相同的
	cout << sizeof(test) <<endl; //8
	return 0}

2、引用的总结

  • 引用的本质就是指针 ,只是编译器弱化了它的功能,所以引用就是弱化了的指针,也叫假指针
  • 指针可以随意乱指,但引用只能指向一个内存地址,从一而终
  • 指针和引用的相同点就是都可以间接的访问与修改变量值
  • 为了能接受各种各样的参数,我们通常使用常引用作为函数形参
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值