博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++解析(4):引用的本质
阅读量:4505 次
发布时间:2019-06-08

本文共 3321 字,大约阅读时间需要 11 分钟。

0.目录

1.

2.

3.

4.

5.

1.引用的意义

  • 引用作为变量別名而存在,因此在一些场合可以代替指针
  • 引用相对于指针来说具有更好的可读性和实用性

1250397-20181205151318993-1223526697.png

注意:函数中的引用形参不需要进行初始化!!!

2.特殊的引用

const引用:

  • 在C++中可以声明const引用
  • const Type& name = var;
  • const引用让变量拥有只读属性

1250397-20181205151350818-715675039.png

当使用常量const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名。

1250397-20181205151457461-1545298014.png
结论:使用常量对const引用初始化后将生成一个只读变量!!!
(引用只能是另一个变量的别名,因此一个引用绝对不可能是一个常量值的别名,换句话说,不能定义一个引用,然后用一个常量对它进行初始化,这里的常量是字面常量。但是有一个例外,就是const引用。我们可以使用字面常量来对const引用进行初始化,编译器在这个时候会真正的产生一个只读变量。由于是只读变量,所以想要直接对它进行赋值肯定会编译出错,但是依旧可以通过指针来改变只读变量的值。)

引用的特殊意义:在C++中,想要一个使已经存在的变量拥有只读属性,变成一个只读变量,只需要定义一个const引用即可。

思考:引用有自己的存储空间吗?

1250397-20181205151618771-1627743904.png

运行下列程序:

#include 
struct TRef{ char& r;};int main(){ char c = 'c'; char& rc = c; TRef ref = { c }; printf("sizeof(char&) = %d\n", sizeof(char&)); printf("sizeof(rc) = %d\n", sizeof(rc)); printf("sizeof(TRef) = %d\n", sizeof(TRef)); printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); return 0;}

输出结果如下:

[root@bogon Desktop]# g++ test.cpp[root@bogon Desktop]# ./a.out sizeof(char&) = 1sizeof(rc) = 1sizeof(TRef) = 8sizeof(ref.r) = 1

(引用所占用的内存空间的大小与指针相同!)

3.引用的本质

引用在C++中的内部实现是一个指针常量

1250397-20181205151633966-1080960146.png

注意:

  1. C++编译器在编译过程中指针常量作为引用的内部实现,因此引用所占用的空间大小和指针相同。
  2. 从使用的角度引用只是一个别名,C++为了是实用性而隐藏了引用的存储空间这一细节。

在C++编译器内部是用指针实现了引用的概念:

#include 
struct TRef{ char* before; char& ref; char* after;};int main(){ char a = 'a'; char& b = a; char c = 'c'; TRef r = {&a, b, &c}; printf("sizeof(r) = %d\n", sizeof(r)); printf("sizeof(r.before) = %d\n", sizeof(r.before)); printf("sizeof(r.after) = %d\n", sizeof(r.after)); printf("&r.before = %p\n", &r.before); printf("&r.after = %p\n", &r.after); return 0;}

运行结果为:

[root@bogon Desktop]# g++ test.cpp[root@bogon Desktop]# ./a.out sizeof(r) = 24sizeof(r.before) = 8sizeof(r.after) = 8&r.before = 0x7ffd9d27af40&r.after = 0x7ffd9d27af50

(在汇编层次,指针和引用的操作是一样的。)

C++中的引用旨在大多数的情况下代替指针

  • 功能性:可以满足多数需要使用指针的场合
  • 安全性:可以避开由于指针操作不当而带来的内存错误
  • 操作性:简单易用,又不失功能强大

4.函数返回引用

分析下面的代码:

#include 
int& demo() // int* const{ int d = 0; printf("demo: d = %d\n", d); return d; // return &d}int& func(){ static int s = 0; printf("func: s = %d\n", s); return s; // return &s}int main(){ int& rd = demo(); int& rs = func(); printf("\n"); printf("main: rd = %d\n", rd); printf("main: rs = %d\n", rs); printf("\n"); rd = 10; rs = 11; demo(); func(); printf("\n"); printf("main: rd = %d\n", rd); printf("main: rs = %d\n", rs); printf("\n"); return 0;}

运行结果为:

[root@bogon Desktop]# g++ test.cpptest.cpp: In function ‘int& demo()’:test.cpp:5: warning: reference to local variable ‘d’ returned[root@bogon Desktop]# ./a.out demo: d = 0func: s = 0main: rd = 53main: rs = 0demo: d = 0func: s = 11main: rd = 53main: rs = 11

demo()函数:

返回了一个局部变量的引用,其实返回了一个局部变量的地址。切记:在使用引用时,我们不用返回局部变量的引用。

func()函数:

依旧想要返回一个局部变量的引用,但是这个局部变量是静态的。因为静态的局部变量它的存储空间是一个全局的存储区,所以它所对应的空间不会由于函数调用的返回而被摧毁,因此这样的做法是没有问题的。

程序编译会有警告(warning):不要返回局部变量的引用。

main函数内部:

rd所代表的变量已经在demo函数返回的时候被摧毁了,这时候rd代表的已经是一个不存在的变量了,从指针的角度来看,这时候rd已经相当于一个野指针了。第31行rd想要对栈上已经被释放的4个字节赋值为10,不但没意义,而且很危险。

结论:

这个例子想告诉大家:引用能在最大的程度上去避开内存操作的错误,但是不能寄希望于有了引用就可以乱来,引用不能完全避免内存方面的错误,原因是引用的本质就是指针。只要它还是指针,必然多多少少还可能遇见指针方面的操作问题,以上就是其中一个最典型的问题。

5.小结

  • 引用作为变量别名而存在旨在代替指针
  • const引用可以使得变量具有只读属性
  • 引用在编译器内部使用指针常量实现
  • 引用的最终本质为指针
  • 引用可以尽可能的避开内存错误

转载于:https://www.cnblogs.com/PyLearn/p/10071492.html

你可能感兴趣的文章
使用 https://git.io 缩短 a GitHub.com URL.
查看>>
拷贝、浅拷贝、深拷贝解答
查看>>
四元数
查看>>
StackAndQueue(栈与队列)
查看>>
URLOS安装、升级、卸载
查看>>
在win7下配置sql2005允许远程访问
查看>>
aspose.cell 设置excel里面的文字是超链接
查看>>
POJ 1067 取石子游戏
查看>>
django开发框架-view & template
查看>>
[Linux]systemd和sysV
查看>>
时间日期正则表达
查看>>
JSON.NET 简单的使用
查看>>
java 集合 HashMap
查看>>
HackerRank "Training the army" - Max Flow
查看>>
jquery next()方法
查看>>
SQLHelper
查看>>
五年修炼SEO、一年五万,多嘛?(看时间如何管理?五点论……)
查看>>
Mesos源码分析(16): mesos-docker-executor的运行
查看>>
echarts柱状图点击阴影部分触发事件
查看>>
3771: Triple
查看>>