引用的本质分析
时间:2019-09-14
本文章向大家介绍引用的本质分析,主要包括引用的本质分析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
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行注释,结果编译报错,原因就是数组的三个元素地址不连续,而是各不相同。
原文地址:https://www.cnblogs.com/songhe364826110/p/11518880.html
- CentOS 6.4 + nginx-1.2.5 + php-5.4.15 + MySQL-5.5.31
- 面试问题 - 只用位操作在ABAP里实现a+b
- JAVA 中异常处理的最佳实践
- webpack 4 升级指北
- 对抗蠕虫 —— 如何让按钮不被 JS 自动点击
- 让你的 git 拥有不同身份
- 购物网站的 redis 相关实现(Java)
- Chrome 常用插件 前端-后端-产品
- 更快更安全,HTTPS 优化总结
- Canvas 动画之支付宝价格拖动选择
- 使用 React 和 GraphQL 做一个todo list
- 徒手撸框架---实现 Aop
- 基于 Token 的 WEB 后台认证机制
- TiDB 源码阅读系列文章(二)初识 TiDB 源码
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- DFS(小白式超详细讲解以及代码讲解)
- 判断一个序列是否有序(升序或者降序)
- 有关dp问题的机器人走地图
- 有关结构体内重载操作的最详细的讲解~~
- 洛谷 P1002 DP解法
- AtCoder Beginner Contest 163 A~~D 详细代码讲解
- HUD 4841 (vector的用法)
- c++ 优先队列(priority_queue)的详细讲解用法
- HDU 1022(关于栈的详细解法)
- count_if函数的用法
- Codeforces Round #633 (Div. 2) A ~~C
- AtCoder Beginner Contest 162 A~~D
- P1036 选数
- P1028 数的计算
- P1598 垂直柱状图