想和高手侃侃而谈C++引用?看这一篇就够了【C++引用】
- 定义
- 应用
- 存在对指针取引用
- 不存在对引用取地址
- 存在指针的指针,不存在引用的引用
- 存在指针数组,不存在引用数组
- 数组的引用
- 常引用
- 常引用的特性
- 临时对象的常引用
- 类型不同的变量常引用本质剖析
- 在能够使用 const 的地方就使用 cosnt
- 引用的本质
- 引用的大小
- 引用的类型
- 结论
- 反汇编比对指针和引用
- 源代码
- 汇编代码比对结果
- 结论
定义
变量名本身是一段内存的引用,也就是别名。
引用是为已有变量起别名。
int a;
int &ra = a;
引用是声明关系,不分配内存空间(宏观)。
引用必须初始化,不能独立存在。
与被别名的变量具有相同数据类型。
代码演示:
#include <iostream>using namespace std;int main()
{int a = 100;int& ra = a;cout << "a = " << a << endl;cout << "ra = " << ra << endl;cout << "&a = " << &a << endl;cout << "&ra = " << &ra << endl;cout << "sizeof(a)= " << sizeof(a) << endl;cout << "sizeof(ra) = " << sizeof(ra) << endl;return 0;
}
运行结果:
值相等,地址相等,大小相等,证明了引用在内存中的别名关系。
可以对引用再次取引用,对一个变量建立多个引用,多个引用之间是等价关系。
代码演示:
#include <iostream>using namespace std;int main()
{int a = 100;int& ra = a;int& rb = a;int& rc = rb;int& rd = rc;cout << "a = " << a << endl;cout << "ra = " << ra << endl;cout << "rb = " << rb << endl;cout << "rc = " << rc << endl;cout << "rd = " << rd << endl;cout << "&a = " << &a << endl;cout << "&ra = " << &ra << endl;cout << "&rb = " << &rb << endl;cout << "&rc = " << &rc << endl;cout << "&rd = " << &rd << endl;cout << "sizeof(a) = " << sizeof(a) << endl;cout << "sizeof(ra) = " << sizeof(ra) << endl;cout << "sizeof(rb) = " << sizeof(rb) << endl;cout << "sizeof(rc) = " << sizeof(rc) << endl;cout << "sizeof(rd) = " << sizeof(rd) << endl;return 0;
}
运行结果:
应用
取代指针传参。
C++可以用引用解决的问题,避免用指针来解决。
代码演示;
#include <iostream>using namespace std;void swapByValue(int a, int b)
{int tmp;tmp = a;a = b;b = tmp;
}void swapByPtr(int* a, int* b)
{int tmp;tmp = *a;*a = *b;*b = tmp;
}void swapByRef(int& a, int& b)
{int tmp;tmp = a;a = b;b = tmp;
}int main()
{int a = 3, b = 5;cout << "a = " << a << " b = " << b << endl;swapByRef(a, b);cout << "a = " << a << " b = " << b << endl;return 0;
}
运行结果:
引用的从宏观上可以理解为:扩展了变量的作用域,传参后,就像在本地解决问题一样。
把一个变量以引用的方式传到另一个作用域,等价于扩展了该变量的作用域。
避免了传 n 级指针,解决 n-1 级指针的问题,即平级内解决问题。
存在对指针取引用
代码演示:
#include <iostream>
using namespace std;void swapByRef(char * & a, char * & b)
{char * tmp;tmp = a;a = b;b = tmp;
}int main()
{char* p = "hello";char* q = "world";cout << "p = " << p << " q = " << q << endl;swapByRef(p, q);cout << "p = " << p << " q = " << q << endl;return 0;
}
运行结果:
不存在对引用取地址
引用的本质:对指针的包装,避免使用裸露的指针。
设计思想:C++避免对引用再次拆封。
代码演示:
#include <iostream>
using namespace std;int main()
{int a;int* p = &a;int* & pr = p;int& ra = a;int& * rpa = &ra;return 0;
}
编译器报错:不允许使用指向引用的指针。
存在指针的指针,不存在引用的引用
指针的指针,即二级指针。
C++为了避免 C 语言设计指针的"失误",避免了引用的引用这种情况,也避免了引用的引用的引用的…的引用的情况。
这种可穷递归的设计本身就是有问题的。
代码演示:存在指针的指针
#include <iostream>
using namespace std;int main()
{int a;int* p = &a;int** pp = &p;int*** ppp = &pp;int**** pppp = &ppp;//无穷无尽return 0;
}
代码演示:不存在引用的引用
#include <iostream>
using namespace std;int main()
{int a;int & p = a;int&& pp = p; //刹车return 0;
}
存在指针数组,不存在引用数组
数组名,本身是首元素的地址,若首元素是引用的话,数组名就成了引用的指针,与上面说过的不符,即不存在引用的指针。
代码演示:存在指针数组
#include <iostream>
using namespace std;int main()
{int a, b, c;int* pArr[] = {&a, &b ,&c};return 0;
}
pArr 代表首元素地址,首元素是 &a 指针,则 pArr 是二级指针。
代码演示:不存在引用数组
#include <iostream>
using namespace std;int main()
{int a, b, c;int & pArr[] = {a, b ,c};return 0;
}
编译器报错:
pArr 代表首元素地址,首元素是变量名a(可以将a理解为对内存的引用),上面说过不允许对引用取地址。
数组的引用
代码演示:
#include <iostream>
using namespace std;int main()
{//数组名的两重性://1:代表整个数组。//2:代表首元素地址。int array[5] = {1,2,3,4,5};int* const & pr = array; //int* const &cout << pr << endl;cout << array << endl;cout << "sizeof(pr) = " << sizeof(pr) << endl;cout << "sizeof(array) = " << sizeof(array) << endl;for (int i = 0; i < 5; i++){cout << pr[i];}cout << endl;int(&ra)[5] = array;cout << ra << endl;cout << array << endl;cout << "sizeof(ra) = " << sizeof(ra) << endl;cout << "sizeof(array) = " << sizeof(array) << endl;for (int i = 0; i < 5; i++){cout << ra[i];}cout << endl;return 0;
}
运行结果:
常引用
C++中 const 定义的变量称为常变量。
const 修饰的变量,有着变量的形式,常量的作用,用作常量,常用于取代#define 定义的宏常量。
#define 宏定义在预处理阶段替换,const 常量在汇编阶段替换。
常引用的特性
const 的本意,即不可修改。所以,const 对象,只能声明为 const 引用,使其语义保持一致性。
no-const 对象,既可以声明为 const 引用,也可以声明为 no-const 引用。
声明为 const 引用,则不可以通过 const 引用修改数据。
代码演示:
#include <iostream>
using namespace std;
int main()
{const int val = 10;//int& rv = val; //err类型不等价const int &rv2 = val;//类型等价int data = 666;int& rd = data;const int& rd2 = data;//非const 对象生命为 const 引用//rd2 = 333; 不可修改cout << "data = " << data << " rd = " << rd << " rd2 = " << rd2 << endl;data = 999; cout << "data = " << data << " rd = " << rd << " rd2 = " << rd2 << endl;return 0;
}
运行结果:
临时对象的常引用
临时对象:不可以取地址的对象。
临时对象:
- CPU中计算产生的中间变量。
- 常量。
- 表达式。
- 函数返回值。
- 类型不同的变量。
代码演示:临时对象的常引用
#include <iostream>using namespace std;//临时变量 即不可取地址的对象int foo()
{int a = 66;return a;
}int main()
{//常量const int& cc = 55;cout << "cc = " << cc << endl;//表达式int a = 3;int b = 5;const int& ret = a + b;cout << "ret = " << ret << endl;//函数返回值const int& ra = foo();cout << "ra = " << ra << endl;//类型不同的变量double d = 100.12;const int& rd = d;cout << "d = " << d << endl;cout << "rd = " << rd << endl;return 0;
}
运行结果:
临时对象的常引用本质:产生中间变量。
类型不同的变量常引用本质剖析
代码演示:
#include <iostream>using namespace std;int main()
{double d = 3.14;//int & t = d; err 类型不等价const int& rd = d; //产生中间变量cout << "d = " << d << endl;cout << "rd = " << rd << endl;d = 4.14;cout << "d = " << d << endl;cout << "rd = " << rd << endl;return 0;
}
运行结果:
本质上 const 引用,引用了一个不可改变的临时变量,
const int tmp = data;
const int & rd = tmp;
此时,我们改变了 data
的值,临时变量 tmp
的值并没有发生改变。
在能够使用 const 的地方就使用 cosnt
好处:
- 避免无意修改数据的编程错误。
- 使用 const 处理 const 和 非cosnt 。否则将只接受 非const数据。
- 使用 cosnt引用,可使函数能够正确的生成并使用临时变量。(实参与引用参数不匹配,就会产生临时变量。)
引用的本质
引用的本质:C++对指针的包装,引用即指针。
引用的大小
代码演示;
#include <iostream>using namespace std;struct TypeP
{char* p;
};struct TypeC
{char c;
};struct TypeR
{char& r;
};int main()
{cout << "sizeof(TypeP) = " << sizeof(TypeP) << endl;cout << "sizeof(TypeC) = " << sizeof(TypeC) << endl;cout << "sizeof(TypeR) = " << sizeof(TypeR) << endl;return 0;
}
运行结果:
引用的类型
C++中只有 const 类型的数据,要求必须初始化。
引用也必须要初始化,所以引用是 const 修饰的指针,一经声明,不可修改。引用的内容可以修改。所以是:type* const p
结论
引用的本质是 const 类型的指针,即 type* const p
。
引用是对指针的封装。
从微观角度来说,引用至少需要分配一个指针类型大小的内存空间。
反汇编比对指针和引用
源代码
代码演示:
#include <iostream>using namespace std;void swapPtr(int* p, int* q)
{int t = *p;*p = *q;*q = t;
}
void swapRef(int& p, int& q)
{int t = p;p = q;q = t;
}
int main()
{int a = 3; int b = 5;swapRef(a, b);swapPtr(&a, &b);return 0;
}
汇编代码比对结果
结论
对同一个功能相同的程序,分别采用了指针和引用的两种方式来进行编写,汇编得到的结果一致。
证明:引用的本质是对指针的封装。
想和高手侃侃而谈C++引用?看这一篇就够了【C++引用】相关推荐
- Pyecharts1.x版本全网最全教程,想学数据可视化的,看这一篇就够了!
前言 本项目整理了目前pyecharts支持的所有图表以及基础配置项- 所有代码均基于Pyecharts v1.7.1版本,均已全部运行通过: 文中代码是基于jupyter notebook中写的,如 ...
- 运维学python用不上_作为运维你还在想要不要学Python,看完这篇文章再说!
原标题:作为运维你还在想要不要学Python,看完这篇文章再说! 本文由马哥教育Python自动化实战班5期学员推荐,转载自简书,作者为Li.Yingjie,内容略经小编改编和加工,观点跟作者无关,最 ...
- 云小课|想实现资源全自动备份?看完这篇秘籍,不再蕉绿~
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:在给备份系统设置& ...
- docker 删除所有镜像_关于 Docker 镜像的操作,看完这篇就够啦 !(下)| 文末福利...
紧接着上篇<关于 Docker 镜像的操作,看完这篇就够啦 !(上)>,奉上下篇 !!! 镜像作为 Docker 三大核心概念中最重要的一个关键词,它有很多操作,是您想学习容器技术不得不掌 ...
- 聊聊Java8之后的JDK升级内容(看这一篇就够了)
聊聊Java8之后的JDK升级内容(看这一篇就够了) 背景 从 JDK 8 到 JDK 17 的新特性 JDK8 回顾 JDK9 JDK10 JDK11 JDK12 JDK13 JDK14 JDK15 ...
- 技术交底书怎么撰写?看这一篇就够了
文章目录 技术交底书怎么撰写?看这一篇就够了 专利技术交底书格式 1. 发明(或实用新型 以下同)的名称 2. 技术领域 3. 背景技术 4. 发明内容 5. 附图说明 6. 具体实施方式 技术交底书 ...
- Fortran保姆级教学——考试所有知识点看这一篇就够了
Fortran保姆级教学--考试所有知识点看这一篇就够了 临近期末本人复习的同时将整个fortran课堂知识整理了下来,希望学弟学妹们今后学这门课的时候不至于在csdn找不到系统的教程,也希望能帮到需 ...
- 17万字 JUC 看这一篇就够了(三) (精华)
今天我们继续来学习Java并发编程 Juc框架 ,把剩余部分学习完 17万字 JUC 看这一篇就够了(一) (精华) 17万字 JUC 看这一篇就够了(二) (精华) 文章目录 非公原理 加锁 解锁 ...
- serviceloader java_【java编程】ServiceLoader使用看这一篇就够了
转载:https://www.jianshu.com/p/7601ba434ff4 想必大家多多少少听过spi,具体的解释我就不多说了.但是它具体是怎么实现的呢?它的原理是什么呢?下面我就围绕这两个问 ...
- 深度好文:云网络丢包故障定位,看这一篇就够了~
深度好文:云网络丢包故障定位,看这一篇就够了~ https://mp.weixin.qq.com/s/-Q1AkxUr9xzGKwUMV-FQhQ Alex 高效运维 今天 来源:本文经授权转自公众号 ...
最新文章
- 解题报告(一)B、(CF453D) Little Pony and Elements of Harmony(FWT经典套路 + 任意模数 k 进制FWT + 快速幂)(2)
- UIActivityViewController: LaunchServices: invalidationHandler called
- tensorrt 低精度推理
- Matlab 条形图实例
- 终于知道PUBWIN2009的数据库用户名与密码已经成功连接
- uc的剪切板能关掉吗_创意手工 | 一张纸折出专属礼品袋,漂亮简单还实用!你爱了吗?!...
- 大数据及hadooop简介
- hibernate - Transaction not successfully started
- 探究position定位中absolute和relative的异同
- 放大分析双缓冲类时间计算问题
- 记录一次es商品模糊查询
- 基于WEB的自行车租赁管理系统设计与实现
- 【MySQL】轻松学习 唯一索引
- Elasticsearch 6.4 ingest-attachment对office文件IK分词器全文检索(1) HttpAPI使用
- android 美团下拉菜单,Android仿美团分类下拉菜单实例代码
- 方舟搭建服务器显示mod出错,为什么我方舟进不去mod服务器 | 手游网游页游攻略大全...
- gcc -c -o编译过程
- 利用阿里云搭建NFS服务器
- 【2020年第七次人口普查】省市县三级人口婚姻状况和妇女生育状况
- widget中自动横竖屏切换时的问题