【c++内存分布系列】单独一个类
首先要明确类型本身是没有具体地址的,它是为了给编译器生成相应对象提供依据。只有编译器生成的对象才有明确的地址。
一、空类
形如下面的类A,类里没有任何成员变量,类的sizeof值为1。
#include <cstdio> class A { };int main(int argc, char** argv) {printf("%d\n", sizeof(A));//类型A的大小 A a;A* pa = &a;printf("%08x\n", pa);//对象a的地址 printf("%02x\n", *pa);//对象a的内容 }
windows输出:
1
0023fc9b
cc
这是因为编译器在编译时为类A插入了一个char型变量,以便确定类A的对象在内存的位置。插入的变量不进行初始化,所以看到内存中的内容为cc
二、包含成员函数但没有成员变量
只包含成员函数没有成员变量的类形式如下:
#include <cstdio> class A { public:void fun(){}; };int main(int argc, char** argv) {printf("%d\n", sizeof(A));//类型A的大小 A a;A* pa = &a;printf("%08x\n", pa);//对象a的地址 printf("%02x\n", *pa);//对象a的内容void (A::*p)();p = &A::fun;printf("%08x\n", p);//成员方法fun的地址 }
windows的输出:
1
001aff23
cc
003211a4
因为成员函数有自己的地址,不随对象而变化,所以类型A的大小依然是1。查看内存003211a4有:
A::fun:
003211A4 E9 17 03 00 00 jmp A::fun (3214C0h)
直接跳转到3214c0h,再查看3214c0h有:
1: #include <cstdio>
2:
3: using namespace std;
4:
5: class A
6: {
7: public:
8: void fun(){};
003214C0 55 push ebp
003214C1 8B EC mov ebp,esp
003214C3 81 EC CC 00 00 00 sub esp,0CCh
即源码的汇编程序。到此应该可以理解为什么成员函数不需要随对象的生成而分配地址了。因为所有对象的方法对应的汇编执行都应该是一样的,所以存在一份足够了,不需要每个对象都重新分配该段代码的空间。
三、包含成员变量
类A包含成员变量形式如下:
#include <cstdio> class A { public:int a;void fun(){}; };int main(int argc, char** argv) {printf("%d\n", sizeof(A));//类型A的大小 A a;a.a = 0xaaaaaaaa;//给成员变量赋值A* pa = &a;printf("%08x\n", pa);//对象a的地址 printf("%08x\n", *pa);//对象a的内容void (A::*p)();p = &A::fun;printf("%08x\n", p);//成员方法fun的地址 }
windows输出:
4
0019fc9c
aaaaaaaa
00b411a4
由于包含成员变量,类A生成的每个对象的成员变量值不一定相同,所以成员变量要随每个对象的生成分配自己的空间。此例的成员变量为int型,所以sizeof类A的值就为类A成员变量占用内存大小的总和,此例为4。如果有更多的成员变量,sizeof的值则为他们的和,内存也依次为他们的值排列,这里就不举例了。查看对象a的内容为0xaaaaaaaa,也验证了该地址存放的是成员变量的值。
四、包含静态方法和静态成员变量
#include <cstdio>class A { public:int a;void fun(){};static int sa;static void sfun(){}; };int A::sa = 0xbbbbbbbb;//给静态成员变量赋值int main(int argc, char** argv) {printf("%d\n", sizeof(A));//类型A的大小 A a;a.a = 0xaaaaaaaa;//给成员变量赋值A* pa = &a;printf("%08x\n", pa);//对象a的地址 printf("%08x\n", *pa);//对象a的内容void (A::*p)();p = &A::fun;printf("%08x\n", p);//成员方法fun的地址 printf("%08x\n", &(A::sa));//静态成员变量的地址printf("%08x\n", A::sa);//静态成员变量的值 printf("%08x\n", A::sfun);//静态方法的地址 }
windows输出:
4
001ffaa0
aaaaaaaa
012011a4
01207000
bbbbbbbb
012011d1
很简单,大家都知道,静态方法和静态变量都有自己的地址,当然也就不会随对象的生成而重新分配,所以sizeof(A)的值依然是4。
去内存0x01207000查看静态成员地址的内容为0xbbbbbbbb,ok没问题,再来看看静态方法内存对应的内容:
10: static int sa;
11: static void sfun(){};
01201580 55 push ebp
01201581 8B EC mov ebp,esp
01201583 81 EC C0 00 00 00 sub esp,0C0h
没错,代码对应的汇编。这下就清晰了。
五、包含虚函数
加入两个虚函数,其他代码不变。
#include <cstdio> class A { public:int a;void fun(){};static int sa;static void sfun(){};virtual void vfun1(){};virtual void vfun2(){}; };int A::sa = 0xbbbbbbbb;//给静态成员变量赋值int main(int argc, char** argv) {printf("%d\n", sizeof(A));//类型A的大小 A a;a.a = 0xaaaaaaaa;//给成员变量赋值A* pa = &a;printf("%08x\n", pa);//对象a的地址 printf("%08x\n", *pa);//对象a的内容void (A::*p)();p = &A::fun;printf("%08x\n", p);//成员方法fun的地址 printf("%08x\n", &(A::sa));//静态成员变量的地址printf("%08x\n", A::sa);//静态成员变量的值 printf("%08x\n", A::sfun);//静态方法的地址 }
windows下输出:
8
0021fa7c
0125574c
012511d6
01257000
bbbbbbbb
01251208
之所以加入两个虚函数,是为了验证编译器只生成了一个虚表,续表的概念不明白的话要查下。也正是因为加入了虚表,内存增加了4,即sizeof(A)的结果变成了8。查看对象a的内存:
0021FA7C 4C
0021FA7D 57
0021FA7E 25 01 AA AA AA
0021FA83 AA
除了0xaaaaaaaa还新增了0x0125574c,这就是虚表的地址。查看该地址内容:
A::`vftable':
0125574C 27
0125574D 11 25 01 86 11 25
01255753 01
包含了两个地址,分别为:0x01251186,0x01251127。这两个地址就是对应两个虚函数,下面只拿其中一个来分析,另一个一样。查看0x01251186:
A::vfun2:
01251186 E9 25 05 00 00 jmp A::vfun2 (12516B0h)
此处直接跳转到vfun2的执行处,内容如下:
13: virtual void vfun2(){};
012516B0 55 push ebp
012516B1 8B EC mov ebp,esp
012516B3 81 EC CC 00 00 00 sub esp,0CCh
012516B9 53 push ebx
没错,vfun2的汇编。
至此一个类的内存所有情况都分析到了,如果该类中包含其他类的对象,那么先计算那个对象的内存占用大小,然后把它当做普通的成员变量看待就可以了。
转载于:https://www.cnblogs.com/budapeng/p/3305132.html
【c++内存分布系列】单独一个类相关推荐
- VS查看C++类的内存分布、初步了解类的内存分布
在VC++项目属性,如下图红线位置,输入 /d1 reportAllClassLayout ,确定: 之后,编译代码,即可查看类的内存分布: 写一个简单C++类,生成,在输出窗口可看到如下: 普通类的 ...
- C++ 如何一次在堆上申请4G的内存?如何设计一个类只能在堆或者栈上创建对象?
1.如何一次在堆上申请4G的内存? 因为32位的环境下虚拟地址空间的大小只有4g,而光内核空间就需要1g,所以不可能申请得到,只有在64位的环境下才可以实现,只需要把执行环境改为64x即可 #incl ...
- C++ 在一个类中用指针的形式申请堆内存构造出另一个类并访问私有成员变量(包括智能指针版本)
一.普通指针版本 代码 #include<iostream> using namespace std;class A {public:A(){ax=2;cout<<" ...
- C++继承时的对象内存位置(一)有成员变量遮蔽时的内存分布
#include <cstdio> #include<iostream> using namespace std; //基类A class A{public:A(int a, ...
- C++ 类的内存分布
C++类内存分布 转自:http://www.cnblogs.com/jerry19880126/p/3616999.html 先写下总结,通过总结下面的例子,你就会明白总结了. 下面总结一下: 1. ...
- C++类内存分布——深度理解继承与虚函数
1.前言与准备 工欲善其事,必先利其器,我们先用好Visual Studio工具,像下面这样一步一步来: 先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 report ...
- 内存分布malloc/calloc/realloc/free/new/delete、内存泄露、String模板、浅拷贝与深拷贝以及模拟string类的实现
内存分布 一.C语言中的动态内存管理方式:malloc/calloc/realloc和free 1.malloc: 从堆上获得指定字节的内存空间,函数声明:void *malloc (int n); ...
- C++类内存分布+钻石模型的解决方法
C++类内存分布 #include<iostream> using namespace std; class Base {private:int val;public:Base(int i ...
- 假定Csomething是一个类,执行下面这些语句后,内存里创建了几个Csomething对象
假定Csomething是一个类,执行下面这些语句后,内存里创建了几个Csomething对象 Csomething a(); Csomething b(2); Csomething c[3]; Cs ...
最新文章
- 看了Linux命令的全称,顿时豁然开朗
- jenkins同平台发布
- MFC GDI绘图基础
- php7.0 百度百科,PHP 7.0.10正式发布
- [6] ADB 文件管理
- MongoDB:有人敢像我一样说出开源的真实用意吗?
- matlab 1到无穷_从零开始的matlab学习笔记——(6)符号计算与极限
- JAVA开发的APP怎么上传_苹果App Store上传应用流程详解
- SQL server增删改查
- windows驱动开发 DDK/WDK/WDM/WDF区别
- 【模拟电子技术基础】学习笔记 第一章 半导体二极管
- 服务器2008r2修改数据,win2008 R2服务器下修改MySQL 5.5数据库data目录的方法
- androidtabhost缓存_Android TabHost用法详解
- vs2010专业版下载链接
- ERROR】Unable to open underlying table which is differently defined or of non-MyISAM type or ...
- on call是什么意思_on call的翻译_音标_读音_用法_例句 - 必应 Bing 词典
- Swift复数计算器
- 2层,3层,4层交换机的区别与特点
- 应对ME23数据抓取时ID发生变化 SAP
- IDEA运行卡顿,网页刷新不及时,网页报404错误以及Ajax收不到servlet返回的数据(已解决)
热门文章
- 你连原理都还没弄明白?java文档注释快捷键idea
- 成功入职字节跳动!2021年冲刺年薪40w
- 【迁移学习(Transfer L)全面指南】Domain-Adversarial Training:基于对抗的迁移学习方法
- Pytorch框架的深度学习优化算法集(优化中的挑战)
- Android移动开发之【Android实战项目】后台服务Service
- 中业科技机器人价格_协作机器人售价持续走低 本土厂商该如何发力
- canvas 文字颜色_实现一个canvas小画板
- 友情链接交换看的不是“真友情”!
- python opencv 边缘检测_opencv-python-学习笔记十四(Canny边缘检测)
- android 颜色资源文件,android中colors.xml颜色设置资源文件的方法