由底层和逻辑说开去--c++之引用的深入剖析
在学c++的时候 我遇到的第一个问题就是这个引用,引用是什么东西,我的c++启蒙教科书是c++ primer plus,这本书上说的是:引用是已定义变量的别名,可以使用这个引用来表示这个变量;每当看到这句话的时候 我就有一种淡淡的的忧伤感,其实还是不懂, 这句话说的意思是说引用是个名字吗,那么引用占多大内存呢,我把这章从头看到尾 可惜对此只字不提, 由此可见写书者的诚意(至少我看不到), 本文就准备解析一下这个引用到底是个什么东东。
先说一下本文的结构,本文主要由三个问题1,引用占内存吗,多大 2.引用跟指针啥关系 3.为什么要发明引用呢?针对这三个问题本文希望从c逻辑和汇编底层层面进行剖析,如有疏漏,还请大家批评指教。
首先第一个问题是引用占内存吗:为了回答这个问题,我们可以用假设验证法;假设1,引用是个名字而已,不占内存,这个我觉得是有些书和老师要传达给我的想法(起码让我这样想了),2.引用占内存,内存大小和它所引用的变量的大小,一样大(这个听起来貌似也有点道理);假设3,引用跟指针一样大,在x86架构上是32位的,就像指针一样(这个是根据引用的作用跟指针相似推出的); 那么我们就写个程序来测一测哪个对喽:
#include <iostream>
using namespace std;
class Data
{
public:
char a;
char & b;
Data():b(a)
{} };
int main()
{
Data d;
cout<<sizeof (d)<<endl;
return 0;
}
看上面的代码,这个对象呢,包含两种类型的变量,一个是char 型的变量另一个是它的引用,那就蛋疼了,这个对象到底是多大的呢;
如果假设1,成立那应该是1,如果假设2成立,那应该是2,如果假设三成立,应该是大于4的倍数也就是8(这里其实是内存对齐机制大家可以测试一个char 和一个int 组成的结构体是8,这个问题更有点意思以后再说) 嗯,那么经过我的测试,这个结果是8个字节,说明啊 这个char & b这个东西啊 占了四个字节 ,这里我们就庆幸没有int型做测试吧;嗯现在弄清了内存方面的事情,就开始思考这个引用变量是个什么东西了,
接着,我们就来看看这个引用它究竟是什么东西,这里我们会对一段cpp代码的结果反编译成汇编来分析分析;
先来一个测试代码
int main()
{
int a=10;
int & b=a;
b=20;
return 0;
}
这么小,能干啥事啊,别担心,虽然这个玩意儿连头文件都没带,但是却能干大事啊 把这段代码编译链接成exe,然后我们学一会儿黑客(黑的是自己),分析一下这个程序的汇编成分;
_main proc near var_8= dword ptr -8
var_4= dword ptr -4
argc= dword ptr 8
argv= dword ptr 0Ch
envp= dword ptr 10h push ebp
mov ebp, esp
sub esp, 8
mov [ebp+var_4], 0Ah
lea eax, [ebp+var_4]
mov [ebp+var_8], eax
mov ecx, [ebp+var_8]
mov dword ptr [ecx], 14h
xor eax, eax
mov esp, ebp
pop ebp
retn
_main endp
这是啥玩意啊,看不懂啊,看不懂没关系,我们可以把上面那个cpp代码换成指针版本的;如下
int main()
{
int a=10;
int * b=&a;
*b=20;
return 0;
}
功能一样是吧,那我们也来反编译一下;
_main proc near var_8= dword ptr -8
var_4= dword ptr -4
argc= dword ptr 8
argv= dword ptr 0Ch
envp= dword ptr 10h push ebp
mov ebp, esp
sub esp, 8
mov [ebp+var_4], 0Ah
lea eax, [ebp+var_4]
mov [ebp+var_8], eax
mov ecx, [ebp+var_8]
mov dword ptr [ecx], 14h
xor eax, eax
mov esp, ebp
pop ebp
retn
_main endp
有没有发现,两个汇编代码居然是一模一样啊,应该没有一点不一样的;虽然不明白它们是什么意思,但是好厉害的样子,起码能够证明引用和指针在底层实现上是等价的,这也就解释了为什么问题1里面引用的内存是4个字节了(X86 架构哦);对于这两段汇编代码(其实是一样的) 如果感兴趣的话,我们可以聊一聊 慢慢的有种渐入佳境的感觉了,下面我们就来从逻辑上分析一下,为什么要有这么一种引用类型呢;
其实这个问题,不是我能回答的,因为c++又不是我发明的,我哪里知道, 但是呢我们可以从编程语言的历史上推测(这是一种科学的猜测方法) , c语言有一个强大的东西叫指针,它的功能强大,又有多种变种,。拿到一个指针,便能操作那块内存了, 但是呢 指针太强大了,容易出错啊,怎么办呢,c++就说咱们来封装一个这个指针吧,虽然不能灭了它,但咱惹不起总躲得起吧; 于是 就发明出引用这种东西,从使用角度上讲增加了许多易用性呢;比如说对int a;操作 指针要 int *pa=a; * pa=123;而引用就不用*
了,而且引用必须在定义的时候赋初值,否则报错,而指针就没错 甚至有些连警告都没有,想想看这多危险啊, 还有引用一经指定对象便不能更改 相当于一个const指针 ,所以我们可以认为引用是对const指针的一种封装 ,好了引用有了 那就灭掉指针吧,不行,因为c++有一种妥协性,怎么说呢,引用受限是为了防止指针使用出错,但是由于指针太强大了,在一些需要指针出现的场合(比如遍历)引用又不能胜任,所以就又保留了指针; 在对指针的封装方面,java做到了,它直接把指针封装成名字了,让人感觉不到指针的存在,完全抛弃了指针
, 那就不能姓c了 于是就叫java吧;
经过上面放分析,我们知道引用就是对const指针的封装,为什么要封装,当然是第一安全,第二快速;就像栈和队列是对链表的封装一样,链表那么强大的东西,封装出来两个小东西,c也一样, 其他语言就是在逻辑层面封装了c , 懂了c再搞这些东西,岂不是 小儿科 ,要是不懂c 不懂内存,那这些我感觉就得跟着感觉走了,理解起来就不是推测而是猜测了; 有人觉得我不懂这些东西也行啊 会用就行了,那这是建立在记忆而不是理解的基础上,等到一段时间不用,再回头来看,我觉得如果之前理解过还是要回忆的快; 所以每当听到有人说c简单
我就觉得莫名其妙 这些东西不都是c玩剩下 的么;
由底层和逻辑说开去--c++之引用的深入剖析的更多相关文章
- 由底层和逻辑说开去——c++之类与对象的深入剖析
类是什么,对象是什么, 这两个问题在各个c++书里面都以一种抽象的描述方式,给了我们近乎完美的答案,然后我好像就知道什么是类什么是对象了,但是当扪心自问,类在哪儿,对象在哪儿,成员方法在哪儿,成员变 ...
- [Objective-C] 从NSInteger说开去
在iOS开发过程中,我一直习惯于使用C语法里的基本类型,而很少用(除非必须使用)Foundation的数据类型.最近看了一些资料,发现自己这样写可能有风险,虽然目前没遇到过相关的问题,但这是非常需要注 ...
- (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去
原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...
- JSP 生命周期 理解JSP底层功能的关键就是去理解它们所遵守的生命周期
JSP 生命周期 理解JSP底层功能的关键就是去理解它们所遵守的生命周期. JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命周期还包括将JSP文件编译成ser ...
- 从Linux内核升级的必要性说开去
Linux内核更新超级频繁,可是有必要时刻升级吗?个人感觉没有必要,可是你要时刻关注新特性列表,然后把自己的内核升级到离最新版本号差一两个月公布的版本号而不是最新版本号.以保证稳定性,由于一两个月的时 ...
- Flask-分开Models解决循环引用
Flask-分开Models解决循环引用 在之前我们测试中,所有语句都在同一个文件中,但随着项目越来越大,管理起来有所不便,所以将Models分离.基本的文件结构如下 \—–app.py\—–mode ...
- 从《BLAME!》说开去——新一代生产级卡通真实感混合的渲染方案
<BLAME!>是Polygon Pictures Inc.(以下简称PPI)创业33周年以来制作的第一部CG剧场电影,故事来自于贰瓶勉的同名漫画作品(中文译名为<探索者>或者 ...
- 由SOAP说开去 - - 谈谈WebServices、RMI、RPC、SOA、REST、XML、JSON
引子: 关于SOAP其实我一直模模糊糊不太理解,这种模模糊糊的感觉表述起来是这样: 在使用web服务时(功能接口),本来我就可以通过安卓中固有的http类(使用http协议),来发送http请求,并且 ...
- 从一个Bug说开去--解决问题的思路,Linked Server, Bulk Insert, DataTable 作为参数传递
声名— 部分内容为杜撰,如有雷同,不胜荣幸! 版权所有,如要引用,请标明出处! 如果打赏,请自便! 1 背景介绍 最近一周在忙一个SQL Server 的Bug,一个简单的Bug,更新两张 ...
随机推荐
- Android(java)学习笔记95:Android原理揭秘系列之View、ViewGroup
作过Android 应用开发的朋友都知道,Android的UI界面都是由View和ViewGroup及其派生类组合而成的.其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器, ...
- 查看Oracle执行计划的几种方法
查看Oracle执行计划的几种方法 一.通过PL/SQL Dev工具 1.直接File->New->Explain Plan Window,在窗口中执行sql可以查看计划结果.其中,Cos ...
- hdu 3440 差分约束
看完题目第一遍,感觉很简单.当写完程序跑测试用例的时候,发现第二个总是过不了,然后好好研究了一下测试用例,才知道原来不是程序有问题,而是我的建图方式错了.对于这些无序的点,如果高的在右边,不等式是di ...
- Linux(CentOS6.5)系统安装Nginx
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口 ...
- Ajax发送FormData对象封装的表单数据
前端页面: <!doctype html> <html lang="en"> <head> <meta charset="UTF ...
- Lombok(1.14.8) - @SneakyThrows
@SneakyThrows @SneakyThrows,声明异常. package com.huey.lombok; import java.io.UnsupportedEncodingExcepti ...
- ibatis.net 多线程的调试
ibatis是一个挺不错的半自动orm框架,从java移植到c# 在ibatis中是支持多线程操作的,但是这几天的使用过程中发现就框架本身任然存在一些问题,可能会让你对多线程的使用并不是那么的顺利 在 ...
- 【转】Android开发中Handler的使用
在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个”下载“按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么 ...
- redis学习-day1
1.nosql数据库的一种. 2.Redis 是一种开源的,先进的key-value存储.它通常被称为数据结构服务器.因为键可以包含字符串.哈希.链表.集合和有序集合. 特点: 3.为了保证效率,数据 ...
- Entity Framework 6.1-Database First介绍
原文:Entity Framework 6.1-Database First介绍 这种方式是比较传统的以数据库为核心的开发模式.比较适合有数据库DBA的团队.或者数据库已存在的情况. 优缺点: 1.优 ...