VS中C++对象的内存布局
本文主要简述一下在Visual Studio中C++对象的内存布局,这里没有什么测试代码,只是以图文的形式来描述一下内存分布,关于测试的代码以及C++对象模型的其他内容大家可以参考一下陈皓先生的几篇博文以及网上的其他一些文章:
《C++虚函数表解析》:http://blog.csdn.net/haoel/article/details/1948051
《C++对象的内存布局(上)》:http://blog.csdn.net/haoel/article/details/3081328
《C++对象的内存布局(下)》:http://blog.csdn.net/haoel/article/details/3081385
根据我自己调试出来的结果来看(Release版本),VS处理对象的原则大致可以分为以下几点:
1、 对于普通成员变量,按照声明次序以及内存的对齐原则存放。
例如对于类A,在32位程序中占据8个字节:
- class A
- {
- public:
- void test() { cout << "A::test\n"; }
- private:
- int nA;
- char chA;
- };
在内存中布局如下:
2、 如果对象含有虚函数,则在对象的开头处添加一个指针,该指针指向一个虚函数表,表中依序存放着该类中虚函数的地址。
例如对于类A,在32位程序中占据12个字节:
- class A
- {
- public:
- virtual void f() { cout << "A::f()\n"; }
- virtual void fA() { cout << "A::fA()\n"; }
- private:
- int nA;
- char chA;
- };
在内存中布局如下,注意的是虚表的最后一项未必是0:
3、 如果对象只有直接继承(非虚拟继承)而来的父类,按照子类以及父类有没有虚函数可以分为以下几种情况:
<1>:子类和父类都没有虚函数,按照继承顺序先放置各个父类部分,再放置子类部分;
例如对于类B,在32位程序中占据24个字节:
- class A1
- {
- private:
- int nA1;
- char chA1;
- };
- class A2
- {
- private:
- int nA2;
- char chA2;
- };
- class B : public A1, public A2
- {
- private:
- int nB;
- char chB;
- };
在内存中布局如下:
<2>:子类有虚函数,父类没有虚函数,则先放置子类的虚函数表指针,再依次放置父类部分,最后放置子类部分;
例如对于类B,在32位程序中占据20个字节:
- class A
- {
- private:
- int nA;
- char chA;
- };
- class B : public A
- {
- public:
- virtual void f() { cout << "B::f()\n"; }
- virtual void fB() { cout << "B::fB()\n"; }
- private:
- int nB;
- char chB;
- };
在内存中布局如下:
<3>:子类和父类都有虚函数,则先把父类列表中带有虚函数的父类放到前面,再依次放置没有虚函数的父类,最后放置子类部分(没有虚函数指针),同时修改各个虚函数表以及指针,使得其满足如下条件:第一个虚函数表指针所指向的虚函数表中先存放继承自本父类的虚函数地址,包括原样继承下来的以及重写的,再放置子类独有的虚函数地址,其余的虚函数表指针所指向的虚函数表只包含继承自对应父类的虚函数地址,包括原样继承下来的以及重写的。
例如对于类B,在32位程序中占据40个字节:
- class A1
- {
- private:
- int nA1;
- char chA1;
- };
- class A2
- {
- public:
- virtual void f() { cout << "A2::f()\n"; }
- virtual void fA2() { cout << "A2::fA2()\n"; }
- private:
- int nA2;
- char chA2;
- };
- class A3
- {
- public:
- virtual void f() { cout << "A3::f()\n"; }
- virtual void fA3() { cout << "A3::fA3()\n"; }
- private:
- int nA3;
- char chA3;
- };
- class B : public A1, public A2, public A3
- {
- public:
- virtual void f() { cout << "B::f()\n"; }
- virtual void fA3() { cout << "B::fA3()\n"; }
- virtual void fB() { cout << "B::fB()\n"; }
- private:
- int nB;
- char chB;
- };
在内存中布局如下:
4、(这一点还不太确定)如果对象有虚基类,无论是自己虚拟继承而来的还是父类虚拟继承而来的,则先按照以上规则将非虚基类部分处理完毕之后,再插入一个指针,再放置该类剩余的成员变量(该指针指向一个表格,表格中的每一项均是一个32位带符号整数——无论32位程序还是64位程序,其中第一项的内容是该指针到本类首部的偏移量,之后依次是该指针到本类虚基类的起始位置的偏移量),当这些全部放置完毕之后,然后再依次放置虚基类。这里有一个问题就是有的时候会在虚基类前面放置一个全零的指针,然而有的时候却又没有,按照我目前测试的结果来看,当子类有构造函数并且只要重写了虚基类的一个函数该虚基类前面就会有这个全零的指针。
例如对于下列程序,在32位程序中占用空间情况分别为:
- class A
- {
- public:
- virtual void f() { cout << "A::f()\n"; }
- virtual void fA() { cout << "A::fA()\n"; }
- private:
- int nA;
- char chA;
- };
- class B : public virtual A
- {
- public:
- virtual void f() { cout << "B::f()\n"; }
- virtual void fB() { cout << "B::fB()\n"; }
- private:
- int nB;
- char chB;
- };
- class C
- {
- public:
- virtual void f() { cout << "C::f()\n"; }
- virtual void fC() { cout << "C::fC()\n"; }
- private:
- int nC;
- char chC;
- };
- class D
- {
- public:
- virtual void f() { cout << "D::f()\n"; }
- virtual void fD() { cout << "D::fD()\n"; }
- private:
- int nD;
- char chD;
- };
- class E
- {
- public:
- virtual void f() { cout << "E::f()\n"; }
- virtual void fE() { cout << "E::fE()\n"; }
- private:
- int nE;
- char chE;
- };
- class F : public virtual B, public virtual C, public D, public virtual E
- {
- public:
- F() {}
- virtual void f() { cout << "F::f()\n"; }
- virtual void fB() { cout << "F::fB()\n"; }
- virtual void fC() { cout << "F::fC()\n"; }
- virtual void fE() { cout << "F::fE()\n"; }
- virtual void fF() { cout << "F::fF()\n"; }
- private:
- int nF;
- char chF;
- };
A、C、D、E均占12个字节,以A为例,内存布局如下:
B占28个字节,在内存中布局如下,值得注意的一点是,此时B因为已经重写了虚基类A的一个虚函数f(),所以如果再显示定义一个构造函数的话,在B中的A部分之前就会添加一个全0的指针,但是如果把构造函数或者f()的重写随便去掉一个,这个全0指针就不会存在了:
F占92个字节,在内存布局中如下,其中虚基类的排列顺序是按照列表里的顺序来的,比如在本例中,F虚拟继承的有B、C、E三个类,所以在虚基类的排放顺序中先放B,又因为B虚拟继承了A,所以最后的顺序是A、B、C、E。还有另外一点就是因为F定义了一个构造函数,所以虚基类前面会有一个全零指针,如果把这个构造函数去掉的话,F的大小就变成了76个字节,正好是92字节减去4个全零指针的大小:
VS中C++对象的内存布局的更多相关文章
- Java对象的内存布局以及对象所需内存大小计算详解
1. 内存布局 在HotSpot虚拟机中,对象的内存布局可以分为三部分:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 1) 对象头(Header): ...
- JVM中,对象在内存中的布局
在hotSpot虚拟机中,对象在内存中的布局可以分成对象头.实例数据.对齐填充三部分. 对象头:主要包括: 1.对象自身的运行行元数据,比如哈希码.GC分代年龄.锁状态标志等,这部分长度在32位虚拟机 ...
- JVM中对象的内存布局与访问定位
一.对象的内存布局 已主流的HotSpot虚拟机来说, 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header).实例数据(Instance Data)和对齐填 ...
- C++ 对象的内存布局(上)
C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击这里查看下篇>>> 前言 07年12月,我写了一篇<C++虚函数表解析>的文 ...
- JVM——深入分析对象的内存布局
概述 一个对象本身的内在结构需要一种描述方式,这个描述信息是以字节码的方法存储在方法区中的.Class本身就是一个对象,都以KB为单位,如果new Integer()为了表示一个数据就占用KB级别的内 ...
- Java对象的内存布局
对象的内存布局 平时用java编写程序,你了解java对象的内存布局么? 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域: 对象头 实例数据 对齐填充 对象头 对象头包括两部分信息: ...
- jvm学习记录-对象的创建、对象的内存布局、对象的访问定位
简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...
- C++ 对象的内存布局(上)
本文转载自haoel博主的博客:陈皓专栏 [空谷幽兰,心如皓月] 原文地址:C++ 对象的内存布局(上) C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击 ...
- JVM总结-java对象的内存布局
在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...
随机推荐
- Map集合重要练习
重要练习:将字符串中的字母按如下格式显示: a(1)b(2)...... 代码及思路如下: /* 获取字符串中字母的次数,并打印出如下格式a(1)b(2)c(3)...... 思路: 先定义一个方法, ...
- asp.net中的绝对路径和相对路径
一.关于相对路径和绝对路径相对路径转绝对路径一般,我们在ASP.NET网站中往往需要把一个相对路径转化为绝对路径.通常是用Server.MapPath()方法.比如网站根目录下有 个"Upl ...
- 关于XMLHttpRequest对象的responseText属性
下面的代码是利用Ajax实现在输入框中写入一个ID号,即时的从数据库中取得数据并在页面无刷新的情况下显示. Ajax.aspx的代码: <%@ Page Language="C#& ...
- Gabor滤波器学习
本文的目的是用C实现生成Gabor模版,并对图像卷积.并简单提一下,Gabor滤波器在纹理特征提取上的应用. 一.什么是Gabor函数(以下内容含部分翻译自维基百科) 在图像处理中,Gabor函数是一 ...
- android 后台运行
改写返回键事件监听,使得back键功能类似home键,让Acitivty退至后台时不被系统销毁,代码如下: public boolean onKeyDown(int keyCode, KeyEvent ...
- UNIX环境高级编程--#include "apue.h"
apue.h头文件为作者自己编写而非系统自带,故需要自行添加! 第一:打开网站 http://www.apuebook.com/第二:选择合适的版本(一共有三个版本,根据书的版本选择)下载源码sour ...
- sql的集合运算
表的加减法 union:使用union 对表进行假发(并集)运算, union等集合运算符通常都会去除重复记录. select shohin_id, shohin_mei from shohin un ...
- js数组(二)
一.位置方法 indexOf()和laseIndexOf() indexOf是从数组的第0项开始向后查找,没有找到返回-1,要求使用=== var numbers = [1,2,3,4,5,4,3,2 ...
- JS中定义类的方法<转>
转载地址:http://blog.csdn.net/sdlfx/article/details/1842218 PS(个人理解): 1) 类通过prototype定义的成员(方法或属性),是每个类对象 ...
- cURL模拟POST方式提交数据
curl_post.php文件: 1 $url = 'http://localhost/test/curl_post_deal.php'; 2 3 $post_data = array( 4 'use ...