引用的本质分析
程序员文章站
2022-12-21 15:14:46
[TOC] 1. 引用的定义 C++新增加了引用的概念: 引用可以看作一个已定义变量的别名 引用的语法 2. 引用的本质 引用在C++中的内部实现是一个常量指针 C++编译器在编译过程中使用常量指针作为引用的内部实现,因此引用所占用的内存大小和指针相同 从使用的角度,引用只是一个别名,C++为了实用 ......
目录
1. 引用的定义
c++新增加了引用的概念:
- 引用可以看作一个已定义变量的别名
- 引用的语法
type &name = var;
int a = 4; int &b = a; //b为a的别名 b = 5; //操作b就是操作a
2. 引用的本质
- 引用在c++中的内部实现是一个常量指针
type &name <==> type *const name
- c++编译器在编译过程中使用常量指针作为引用的内部实现,因此引用所占用的内存大小和指针相同
- 从使用的角度,引用只是一个别名,c++为了实用性而隐藏了引用的存储空间这一细节
#include <cstdio> struct tref { char &r; }; int main(int argc, char *argv[]) { char c = 'c'; char &rc = c; tref ref = { c }; printf("sizeof(rc) = %d\n", sizeof(rc)); printf("sizeof(tref) = %d\n", sizeof(tref)); printf("sizeof(ref) = %d\n", sizeof(ref)); printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); /*sizeof(type &)的大小,就是type类型的大小*/ printf("sizeof(char &) = %d\n", sizeof(char &)); printf("sizeof(int &) = %d\n", sizeof(int &)); printf("sizeof(double &) = %d\n", sizeof(double &)); return 0; }
#include <stdio.h> struct tref { char *before; char &ref; char *after; }; int main(int argc, char *argv[]) { 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; }
3. 引用的意义
- c++中的引用作为变量别名而存在,旨在大多数的情况下代替指针
- 引用可以满足绝大多数需要使用指针的场合
- 引用可以避开由于指针操作不当而带来的内存错误
- 引用相对于指针来说具有更好的可读性和实用性
注意:由于引用的内部实现为指针,因此函数不能返回非静态局部变量的引用
#include <stdio.h> int &demo() { int d = 0; printf("demo: d = %d\n", d); return d; } int &func() { static int s = 0; printf("func: s = %d\n", s); return s; } int main(int argc, char *argv[]) { 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; }
4. 特殊的引用—const引用
- 在c++中可以声明const引用
const type &name = var
- 可以使用const常量、变量、字面值常量对const引用初始化
- 不管使用何种方式初始化,const引用都将产生一个只读变量
- 当使用字面值常量对const引用初始化时,c++编译器会为常量值分配内存空间,并将引用作为这段内存空间的别名
const引用类型 vs 初始化变量类型
- 类型相同,const引用的就是初始化变量
- 类型不同,const引用的不是初始化变量,而是初始化变量的临时对象
注意:const只是修饰符,不代表类型,也就是说,const int和int是相同类型。
#include <stdio.h> int main() { const int a = 3; int b = 4; char c = 'c'; const int &ra = a; const int &rb = b; const int &rc = c; const int &rd = 1; int *p1 = (int *)&ra; int *p2 = (int *)&rb; int *p3 = (int *)&rc; int *p4 = (int *)&rd; *p1 = 5; *p2 = 6; *p3 = 7; *p4 = 8; printf("ra = %d\n", ra); printf("rb = %d\n", rb); printf("rc = %d\n", rc); printf("rd = %d\n", rd); printf("\n"); printf("b = %d\n", b); //b的类型和rb相同,rb引用的就是b,所以改变rb的值,b也跟着一起改变 printf("c = %c\n", c); //c的类型和rc不同,rb引用的是c的临时对象,所以改变rc的值,c不受影响 return 0; }
5. 引用和指针的关系
指针 | 引用 |
---|---|
指针是一个变量,其值为一个内存地址 | 引用是一个变量的新名字 |
指针可以不初始化,而是在使用时赋值 | 引用必须在定义时初始化 |
通过指针可以访问对应内存地址中的值 | 对引用的操作(赋值、取地址等)会传递到代表的变量上 |
指针可以保存不同的地址 | 引用在初始化之后无法代表其他变量 |
指针可以被const修饰,成为常量或只读变量 | const引用使其代表的变量具有只读属性 |
在工程项目开发中
- 当进行c++编程时,直接站在使用的角度,引用和指针没有任何关系
- 当对c++代码进行调试分析时,一些特殊情况,可以考虑站在c++编译器的角度,引用在内部实现为指针常量
我们给出一个站在c++编译器的角度看待引用的示例,下面这段代码有问题吗?
int a = 1; int b = 2; int *pc = new int(3); int &array[] = {a, b, *pc};
- 数组是一片连续的内存空间
- 引用数组会破坏该特性,各元素代表的变量可能存储在不同的位置
- 因此,c++不支持引用数组!!!!!!
#include <stdio.h> int a = 1; struct sv { int &x; int &y; int &z; }; int main() { int b = 2; int *c = new int(3); sv sv = {a, b, *c}; int &array[] = {a, b, *c}; //&array[1] - &array[0] != 4,编译报错 printf("&sv.x = %p\n", &sv.x); printf("&sv.y = %p\n", &sv.y); printf("&sv.z = %p\n", &sv.z); delete c; return 0; }
首先,注释掉代码第17行,编译运行结果如下,可以看出打印出的内存地址是各不相同的。
然后,去除代码第17行注释,结果编译报错,原因就是数组的三个元素地址不连续,而是各不相同。
推荐阅读
-
详解用ELK来分析Nginx服务器日志的方法
-
PHP中获取变量的变量名的一段代码的bug分析
-
php面向对象全攻略 (三)特殊的引用“$this”的使用
-
AngularJS的依赖注入实例分析(使用module和injector)
-
AngularJS中$injector、$rootScope和$scope的概念和关联关系深入分析
-
LotusPhp笔记之:基于ObjectUtil组件的使用分析
-
企业网站优化想要达到最佳效果的关键点分析
-
PHP基于timestamp和nonce实现的防止重放攻击方案分析
-
python 随机数生成的代码的详细分析
-
Yii框架使用PHPExcel导出Excel文件的方法分析【改进版】