【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的汇编。
至此一个类的内存所有情况都分析到了,如果该类中包含其他类的对象,那么先计算那个对象的内存占用大小,然后把它当做普通的成员变量看待就可以了。
【c++内存分布系列】单独一个类的更多相关文章
- 【c++内存分布系列】虚基类表
虚基类表相对于虚函数表要稍微难理解些,故单独提出来. 虚函数表是在对象生成时插入一个虚函数指针,指向虚函数表,这个表中所列就是虚函数. 虚基类表原理与虚函数表类似,不过虚基类表的内容有所不同.表的第一 ...
- 【c++内存分布系列】单继承
父类包括成员函数.静态函数.静态方法,子类包括成员函数.静态函数.静态方法的情况与一个类时完全一致,这里就不做分析了.子类单独包含虚函数时继承无关,也不做分析了. 一.父类子类都为空 #include ...
- c++内存分布之虚函数(单一继承)
系列 c++内存分布之虚函数(单一继承) [本文] c++内存分布之虚函数(多继承) 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...
- c++内存分布之虚析构函数
关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...
- C++对象内存分布(3) - 菱形继承(virtual)
1.前言 本篇文章的全部代码样例.假设是windows上编译执行.则使用的是visual studio 2013.假设是RHEL6.5平台(linux kernal: 2.6.32-431.el6.i ...
- C++对象内存分布详解(包括字节对齐和虚函数表)
转自:https://www.jb51.net/article/101122.htm 1.C++对象的内存分布和虚函数表: C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数 ...
- VC++中的类的内存分布(上)(通过强制转换,观察地址,以及地址里的值来判断)
0.序 目前正在学习C++中,对于C++的类及其类的实现原理也挺感兴趣.于是打算通过观察类在内存中的分布更好地理解类的实现.因为其实类的分布是由编译器决定的,而本次试验使用的编译器为VS2015 RC ...
- C++类内存分布
http://www.cnblogs.com/jerry19880126/p/3616999.html#undefined 书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看 ...
- C++ 类的内存分布
C++类内存分布 转自:http://www.cnblogs.com/jerry19880126/p/3616999.html 先写下总结,通过总结下面的例子,你就会明白总结了. 下面总结一下: ...
随机推荐
- python正则表达式——re模块
http://blog.csdn.net/zm2714/article/details/8016323 re模块 开始使用re Python通过re模块提供对正则表达式的支持.使用re的一般步骤是先将 ...
- ***redis linux 命令使用总结
redis命令参考: http://redisdoc.com/ 1. [root@iZ25rloipcsZ src]# redis-cli-bash: redis-cli: command not f ...
- 用cxSelect插件补充一下回显过滤项功能
这个在DJANGO里,最好在过滤之后,让用户知道自己过滤的选择.所以要定位默认值. 1,在HTML文件里显示默认值: <form class="uk-form" name=& ...
- SQLite数据库的体系结构(翻译自sqlite.org)
$1 简介 本文档描述了SQLite库的体系结构,这些信息对那些想理解和修改SQLite的内部工作机制的人是有用的. 下图显示了SQLite的主要组成部件及其相互关系,下面的内容将描述每一 ...
- 255. Verify Preorder Sequence in Binary Search Tree
题目: Given an array of numbers, verify whether it is the correct preorder traversal sequence of a bin ...
- Redis是什么?
1. Redis是什么 这个问题的结果影响了我们怎么用Redis.如果你认为Redis是一个key value store, 那可能会用它来代替MySQL;如果认为它是一个可以持久化的cache, 可 ...
- Hibernate检索方式 分类: SSH框架 2015-07-10 22:10 4人阅读 评论(0) 收藏
我们在项目应用中对数据进行最多的操作就是查询,数据的查询在所有ORM框架中也占有极其重要的地位.那么,如何利用Hibernate查询数据呢?Hibernate为我们提供了多种数据查询的方式,又称为Hi ...
- sql partition by 的使用
select a.bs_sn, a.bs_bd_no, a.bs_bk_code, a.bs_kind_no, a.bs_flag, b.det_flag, c.bp_in_no, c.bp_name ...
- Android开发之ActivityManager获取系统信息
1.判断指定的service是否在运行 public static boolean isServiceRunning(Context ctx, String serviceName) { Activi ...
- maven - setting.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Soft ...