原来有些东西真的不存在

一直好奇c++这类语言的类,在底层上是如何实现的,没有对象,所以不理解面向对象的语言,不理解它是如何保证private和protected下的变量是不被外部访问的。
尝试过去在网上寻找这些问题的答案,但是涉及到私有变量的文章,基本都是只说它的特性,找不到一篇文章说它在底层是如何实现的,后面我想起,我好像也会汇编呀,然后就写了一个demo,然后反汇编,它瞬间刷新了我的世界观。

变量是如何实现的

在c下,我们使用到的语言,大致为局部变量,静态变量,全局变量和寄存器变量。

寄存器变量

除了寄存器变量,其他变量,都是把数据存放在内存里的,而寄存器变量,在底层实际分配的是一个寄存器,它有可能是ax,也有可能是bx,在计算机中,通用寄存器是有限的,也就是在程序里不能声明太多寄存器变量同时使用,如果你在一个模块里声明过多的寄存器变量,一般编译器会对这些内容进行优化,把不常使用的变量,分配到内存里去。
因为分配的是寄存器,所以寄存器变量,不能进行取址这类的指针操作,因为它本身就在cpu里,没有地址。指针实际就是一个访问内存的地址,它是发送在cpu地址总线上的值,地址总线又是cpu内部的一个寄存器控制的,在实模式下,指针就是一个真实的物理内存地址,只是挂在地址总线上的未必是内存,也就意味着指针也适合用于访问挂在地址总线上的外设。因为有mmu,在保护模式下,指针就未必是一个真实的物理内存地址了。

内存变量

全局变量和静态变量,记得好像是由系统来分配的,而局部变量,是由程序代码来分配的,基本都是在栈区分配。
栈区是一段特殊的内存地址,但在物理上,它跟其他内存是没有分别的,特殊之处是因为cpu内部有一个栈基址和一个栈指针,两个寄存器共同协助cpu对栈进行访问,在数据结构上,它跟队列比较相近,在实现上,它是跟虫子一样蠕动的,在一个函数被调用后,函数自身会把栈基址(push %bp)入栈,然后把栈指针的值给栈基址(mov %sp,%bp),sp就像虫子的头部,bp就像虫子的尾部,当函数运行完成后,就逆向操作,于是释放了函数所占用的栈内存,一般栈是向下生长的,也就是bp指向的地址,大于sp指向的地址。
在函数内声明的变量,除了静态变量和寄存器变量,或者是动态分配的变量,都是分配在栈里的,编译器会统计改函数里所有局部变量所占的空间,比如改函数所有局部变量占了16字节,那么程序在完成栈蠕动后,会一次性分配这部分空间(sub 0x10,%sp)因为栈是向下生长的,所以这里是减,如果栈是向上生长的,这里得用add。访问变量就由bp-offset,比如第一个变量是一个int的age,那么访问时,比如age=32,就是mov 0x20,(%bp)在之前的汇编标准是[%bp]表示访问该寄存器的值为地址的内存空间。

类里的变量

实际上,无论是公有变量,还是私有变量,类里声明的局部变量,都在该类实例时所在的函数的栈里分配。
类的成员变量和成员函数,是分开实现的,成员变量是通过与c语言一样的方法进行分配的,而成员函数的实现方法,跟一般函数的实现方法并没有区别,只是同一个类的实例,只在程序里生成一个成员函数,只是在程序声明了一个类实例后,会把分配给该类实例的变量地址,作为参数,传递给构造方法,这个应该就是所谓的this指针。
而类里的公有变量,实际跟局部变量一样,是通过bp-offset去访问的。

所以,所谓的public和private实际并不存在,在目标程序里不存在,并不意味着它没用,很显然它是给编译器检查用的。在汇编层面上,类的实现,跟用struct声明结构体,然后声明以该结构体指针为参数的函数,并没有什么区别,但在项目管理和控制上,类还是有很多优点的,并且类的继承特性,不需要重新写一个以新结构体为参数的函数,因为这个被封装到this里了。

测试代码

int main()
{
	classdemo mydemo;
	classdemo mydemo2;
	myroot.newage = 129;
	return 0;
}
0000000000000055 <main>:
  55:	55                   	push   %rbp
  56:	53                   	push   %rbx
  57:	48 83 ec 48          	sub    $0x48,%rsp
  5b:	48 8d ac 24 80 00 00 	lea    0x80(%rsp),%rbp
  62:	00 
  63:	e8 00 00 00 00       	callq  68 <main+0x13>
  68:	48 8d 45 b0          	lea    -0x50(%rbp),%rax
  6c:	48 89 c1             	mov    %rax,%rcx
  6f:	e8 8c ff ff ff       	callq  0 <_ZN8classdemoC1Ev>
  74:	48 8d 45 a0          	lea    -0x60(%rbp),%rax
  78:	48 89 c1             	mov    %rax,%rcx
  7b:	e8 80 ff ff ff       	callq  0 <_ZN8classdemoC1Ev>
  80:	c7 45 b0 81 00 00 00 	movl   $0x81,-0x50(%rbp)
  87:	bb 00 00 00 00       	mov    $0x0,%ebx

代码有些长,切了其中一部分,在return 0的实现之后,main还有一部分所谓的析构函数的调用
最后才到

  aa:	5b                   	pop    %rbx
  ab:	5d                   	pop    %rbp
  ac:	c3                   	retq   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值