首先要明确类型本身是没有具体地址的,它是为了给编译器生成相应对象提供依据。只有编译器生成的对象才有明确的地址。

  一、空类

  形如下面的类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++内存分布系列】单独一个类的更多相关文章

  1. 【c++内存分布系列】虚基类表

    虚基类表相对于虚函数表要稍微难理解些,故单独提出来. 虚函数表是在对象生成时插入一个虚函数指针,指向虚函数表,这个表中所列就是虚函数. 虚基类表原理与虚函数表类似,不过虚基类表的内容有所不同.表的第一 ...

  2. 【c++内存分布系列】单继承

    父类包括成员函数.静态函数.静态方法,子类包括成员函数.静态函数.静态方法的情况与一个类时完全一致,这里就不做分析了.子类单独包含虚函数时继承无关,也不做分析了. 一.父类子类都为空 #include ...

  3. c++内存分布之虚函数(单一继承)

    系列 c++内存分布之虚函数(单一继承) [本文] c++内存分布之虚函数(多继承) 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...

  4. c++内存分布之虚析构函数

    关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...

  5. C++对象内存分布(3) - 菱形继承(virtual)

    1.前言 本篇文章的全部代码样例.假设是windows上编译执行.则使用的是visual studio 2013.假设是RHEL6.5平台(linux kernal: 2.6.32-431.el6.i ...

  6. C++对象内存分布详解(包括字节对齐和虚函数表)

    转自:https://www.jb51.net/article/101122.htm 1.C++对象的内存分布和虚函数表: C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数 ...

  7. VC++中的类的内存分布(上)(通过强制转换,观察地址,以及地址里的值来判断)

    0.序 目前正在学习C++中,对于C++的类及其类的实现原理也挺感兴趣.于是打算通过观察类在内存中的分布更好地理解类的实现.因为其实类的分布是由编译器决定的,而本次试验使用的编译器为VS2015 RC ...

  8. C++类内存分布

    http://www.cnblogs.com/jerry19880126/p/3616999.html#undefined 书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看 ...

  9. C++ 类的内存分布

    C++类内存分布 转自:http://www.cnblogs.com/jerry19880126/p/3616999.html   先写下总结,通过总结下面的例子,你就会明白总结了. 下面总结一下: ...

随机推荐

  1. 国产神通数据库操作备忘(Linux)

    最近接触到国产神通数据库的一个项目,发现这个数据库还是挺有技术含量的,看起来做的还不错. 1.启动停止 在终端窗口中输入以下命令启动数据库: # /etc/init.d/oscardb_<数据库 ...

  2. JsRender系列demo(4)-if else

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  3. PCB板的价格是怎么算出来的?

    Part 1 :影响一块PCB板价格的各种因素 PCB的价格是很多采购者一直很困惑的事情,很多人在线下单时也会疑问这些价格是怎么算出来的,下面我们就一起谈论一下PCB价格的组成因素. 1.PCB所用材 ...

  4. WCF入门(五)---创建WCF服务

    使用Microsoft Visual Studio2012创建WCF服务,理解如下所有必要的编码,更好地创建WCF服务的概念,这里做一个简单的任务. 启动Visual Studio 2012. 单击新 ...

  5. C++:类的组合

    在类中定义的数据成员一般都是基本数据类型或服复合数据类型.但是还可以根据需要使用 其他类的对象作为正在声明的数据成员.复杂的对象还可以有比较简单的对象一某种方式组合 而成,复杂对象和组成它的简单对象之 ...

  6. OSSEC配置文件ossec.conf中添加mysql服务

    配置路径:/opt/ossec/etc/ossec.conf <ossec_config>   <global>     <email_notification>y ...

  7. Android设置TextView显示一行或多行

    在listView的item中或者是特殊的业务需求中,会要求TextView的内容不完全显示,只有通过一个指定的操作后才显示所有的,比如说一个按钮或者是其它的什么控件. 要想实现这个效果并不难,只要控 ...

  8. 转Java 回调函数的理解

    所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数.例如Win32下的窗口过程函数就是一个典型的回调函数.一般说来,C ...

  9. selenium--大家庭介绍

    安装好配置环境后,开始我的selenium之旅.简单的了解一下色,selenium大家庭的组成. Selenium是ThoughtWorks专门为 Web 应用而开发的功能测试工具.Selenium使 ...

  10. Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释

    上一篇说的是一个简单的应用,说明JNI是怎么工作的,这一篇主要来说一下,那个本地方法sayHello的参数的说明,以及其中方法的使用 首先来看一下C++中的sayHello方法的实现: JNIEXPO ...