本文主要简述一下在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个字节:

  1. class A
  2.  
  3. {
  4.  
  5. public:
  6.  
  7. void test() { cout << "A::test\n"; }
  8.  
  9. private:
  10.  
  11. int nA;
  12.  
  13. char chA;
  14.  
  15. };

在内存中布局如下:

2、  如果对象含有虚函数,则在对象的开头处添加一个指针,该指针指向一个虚函数表,表中依序存放着该类中虚函数的地址。

例如对于类A,在32位程序中占据12个字节:

  1. class A
  2.  
  3. {
  4.  
  5. public:
  6.  
  7. virtual void f() { cout << "A::f()\n"; }
  8.  
  9. virtual void fA() { cout << "A::fA()\n"; }
  10.  
  11. private:
  12.  
  13. int nA;
  14.  
  15. char chA;
  16.  
  17. };

在内存中布局如下,注意的是虚表的最后一项未必是0:

3、  如果对象只有直接继承(非虚拟继承)而来的父类,按照子类以及父类有没有虚函数可以分为以下几种情况:

<1>:子类和父类都没有虚函数,按照继承顺序先放置各个父类部分,再放置子类部分;

例如对于类B,在32位程序中占据24个字节:

  1. class A1
  2.  
  3. {
  4.  
  5. private:
  6.  
  7. int nA1;
  8.  
  9. char chA1;
  10.  
  11. };
  12.  
  13. class A2
  14.  
  15. {
  16.  
  17. private:
  18.  
  19. int nA2;
  20.  
  21. char chA2;
  22.  
  23. };
  24.  
  25. class B : public A1, public A2
  26.  
  27. {
  28.  
  29. private:
  30.  
  31. int nB;
  32.  
  33. char chB;
  34.  
  35. };

在内存中布局如下:

<2>:子类有虚函数,父类没有虚函数,则先放置子类的虚函数表指针,再依次放置父类部分,最后放置子类部分;

例如对于类B,在32位程序中占据20个字节:

  1. class A
  2.  
  3. {
  4.  
  5. private:
  6.  
  7. int nA;
  8.  
  9. char chA;
  10.  
  11. };
  12.  
  13. class B : public A
  14.  
  15. {
  16.  
  17. public:
  18.  
  19. virtual void f() { cout << "B::f()\n"; }
  20.  
  21. virtual void fB() { cout << "B::fB()\n"; }
  22.  
  23. private:
  24.  
  25. int nB;
  26.  
  27. char chB;
  28.  
  29. };

在内存中布局如下:

<3>:子类和父类都有虚函数,则先把父类列表中带有虚函数的父类放到前面,再依次放置没有虚函数的父类,最后放置子类部分(没有虚函数指针),同时修改各个虚函数表以及指针,使得其满足如下条件:第一个虚函数表指针所指向的虚函数表中先存放继承自本父类的虚函数地址,包括原样继承下来的以及重写的,再放置子类独有的虚函数地址,其余的虚函数表指针所指向的虚函数表只包含继承自对应父类的虚函数地址,包括原样继承下来的以及重写的。

例如对于类B,在32位程序中占据40个字节:

  1. class A1
  2.  
  3. {
  4.  
  5. private:
  6.  
  7. int nA1;
  8.  
  9. char chA1;
  10.  
  11. };
  12.  
  13. class A2
  14.  
  15. {
  16.  
  17. public:
  18.  
  19. virtual void f() { cout << "A2::f()\n"; }
  20.  
  21. virtual void fA2() { cout << "A2::fA2()\n"; }
  22.  
  23. private:
  24.  
  25. int nA2;
  26.  
  27. char chA2;
  28.  
  29. };
  30.  
  31. class A3
  32.  
  33. {
  34.  
  35. public:
  36.  
  37. virtual void f() { cout << "A3::f()\n"; }
  38.  
  39. virtual void fA3() { cout << "A3::fA3()\n"; }
  40.  
  41. private:
  42.  
  43. int nA3;
  44.  
  45. char chA3;
  46.  
  47. };
  48.  
  49. class B : public A1, public A2, public A3
  50.  
  51. {
  52.  
  53. public:
  54.  
  55. virtual void f() { cout << "B::f()\n"; }
  56.  
  57. virtual void fA3() { cout << "B::fA3()\n"; }
  58.  
  59. virtual void fB() { cout << "B::fB()\n"; }
  60.  
  61. private:
  62.  
  63. int nB;
  64.  
  65. char chB;
  66.  
  67. };

在内存中布局如下:

4、(这一点还不太确定)如果对象有虚基类,无论是自己虚拟继承而来的还是父类虚拟继承而来的,则先按照以上规则将非虚基类部分处理完毕之后,再插入一个指针,再放置该类剩余的成员变量(该指针指向一个表格,表格中的每一项均是一个32位带符号整数——无论32位程序还是64位程序,其中第一项的内容是该指针到本类首部的偏移量,之后依次是该指针到本类虚基类的起始位置的偏移量),当这些全部放置完毕之后,然后再依次放置虚基类。这里有一个问题就是有的时候会在虚基类前面放置一个全零的指针,然而有的时候却又没有,按照我目前测试的结果来看,当子类有构造函数并且只要重写了虚基类的一个函数该虚基类前面就会有这个全零的指针。

例如对于下列程序,在32位程序中占用空间情况分别为:

  1. class A
  2.  
  3. {
  4.  
  5. public:
  6.  
  7. virtual void f() { cout << "A::f()\n"; }
  8.  
  9. virtual void fA() { cout << "A::fA()\n"; }
  10.  
  11. private:
  12.  
  13. int nA;
  14.  
  15. char chA;
  16.  
  17. };
  18.  
  19. class B : public virtual A
  20.  
  21. {
  22.  
  23. public:
  24.  
  25. virtual void f() { cout << "B::f()\n"; }
  26.  
  27. virtual void fB() { cout << "B::fB()\n"; }
  28.  
  29. private:
  30.  
  31. int nB;
  32.  
  33. char chB;
  34.  
  35. };
  36.  
  37. class C
  38.  
  39. {
  40.  
  41. public:
  42.  
  43. virtual void f() { cout << "C::f()\n"; }
  44.  
  45. virtual void fC() { cout << "C::fC()\n"; }
  46.  
  47. private:
  48.  
  49. int nC;
  50.  
  51. char chC;
  52.  
  53. };
  54.  
  55. class D
  56.  
  57. {
  58.  
  59. public:
  60.  
  61. virtual void f() { cout << "D::f()\n"; }
  62.  
  63. virtual void fD() { cout << "D::fD()\n"; }
  64.  
  65. private:
  66.  
  67. int nD;
  68.  
  69. char chD;
  70.  
  71. };
  72.  
  73. class E
  74.  
  75. {
  76.  
  77. public:
  78.  
  79. virtual void f() { cout << "E::f()\n"; }
  80.  
  81. virtual void fE() { cout << "E::fE()\n"; }
  82.  
  83. private:
  84.  
  85. int nE;
  86.  
  87. char chE;
  88.  
  89. };
  90.  
  91. class F : public virtual B, public virtual C, public D, public virtual E
  92.  
  93. {
  94.  
  95. public:
  96.  
  97. F() {}
  98.  
  99. virtual void f() { cout << "F::f()\n"; }
  100.  
  101. virtual void fB() { cout << "F::fB()\n"; }
  102.  
  103. virtual void fC() { cout << "F::fC()\n"; }
  104.  
  105. virtual void fE() { cout << "F::fE()\n"; }
  106.  
  107. virtual void fF() { cout << "F::fF()\n"; }
  108.  
  109. private:
  110.  
  111. int nF;
  112.  
  113. char chF;
  114.  
  115. };

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++对象的内存布局的更多相关文章

  1. Java对象的内存布局以及对象所需内存大小计算详解

    1. 内存布局 在HotSpot虚拟机中,对象的内存布局可以分为三部分:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 1) 对象头(Header): ...

  2. JVM中,对象在内存中的布局

    在hotSpot虚拟机中,对象在内存中的布局可以分成对象头.实例数据.对齐填充三部分. 对象头:主要包括: 1.对象自身的运行行元数据,比如哈希码.GC分代年龄.锁状态标志等,这部分长度在32位虚拟机 ...

  3. JVM中对象的内存布局与访问定位

      一.对象的内存布局 已主流的HotSpot虚拟机来说,   在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header).实例数据(Instance Data)和对齐填 ...

  4. C++ 对象的内存布局(上)

    C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击这里查看下篇>>> 前言 07年12月,我写了一篇<C++虚函数表解析>的文 ...

  5. JVM——深入分析对象的内存布局

    概述 一个对象本身的内在结构需要一种描述方式,这个描述信息是以字节码的方法存储在方法区中的.Class本身就是一个对象,都以KB为单位,如果new Integer()为了表示一个数据就占用KB级别的内 ...

  6. Java对象的内存布局

    对象的内存布局 平时用java编写程序,你了解java对象的内存布局么? 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域: 对象头 实例数据 对齐填充 对象头 对象头包括两部分信息: ...

  7. jvm学习记录-对象的创建、对象的内存布局、对象的访问定位

    简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...

  8. C++ 对象的内存布局(上)

    本文转载自haoel博主的博客:陈皓专栏 [空谷幽兰,心如皓月] 原文地址:C++ 对象的内存布局(上) C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击 ...

  9. JVM总结-java对象的内存布局

    在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...

随机推荐

  1. OA 权限控制

    对于加入删除 初始化password等操作的权限 控制 第一种方法就是在每一个超链接前加 推断 如 <s:if test="#session.user.hasPrivilegeByNa ...

  2. q.js实现nodejs顺序调用

    nodejs的异步调用有时候是最让人头疼的,如何能是一些代码顺序的执行呢,这里和大家分享nodejs的promise 什么是promise promise一个标准,它描述了异步调用的返回结果,包括正确 ...

  3. 关于UIScrollView属性跟方法的总结

    iOS中UIScollView的总结 在iOS开发中可以说UIScollView是所有滑动类视图的基础,包括UITableView,UIWebView,UICollectionView等等,UIScr ...

  4. HDU2094(产生冠军)题解

    HDU2094(产生冠军)题解 以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 根据给定现有比赛结果推断分析冠军.(这描述...我建议还是看题吧,题不长) [题目分析] 给出的 ...

  5. Oracle中字段的修改操作语法

      对字段操作 操作方法 更新字段名 alter table TABLE_NAME rename column column_old to column_new; 添加字段 alter table T ...

  6. mysql性能优化学习笔记(2)如何发现有问题的sql

    一.使用mysql慢查询日志对有效率问题的sql进行监控      1)开启慢查询       show variables like ‘slow_query_log’;//查看是否开启慢查询日志   ...

  7. "创业"半年

                作为一个程序员, 因为受够了”给别人实现梦想太累”的念头, 又受到”外面给出更高薪水”的诱惑, 果断离职创业. 但原本是要创业的, 过了半年, 变成了失业, 这其中到底经历了哪 ...

  8. axis2 webservices 411错误解决办法

    错误:org.apache.axis2.AxisFault: Transport error: 411 Error: Length Required 可能会导致这个问题的原因: 1.访问地址经过端口映 ...

  9. 异步操作AsycnTask类

    1.  首先执行onPreExecute方法,进行UI的初步设置 2.  其次执行doInBackground方法,此时将不在UI中线程中进行了 3.  然后如果要进行中的数据的话可以通过publis ...

  10. 为啥都不用Qt Quick Controls 2呢

     为啥都不用Qt Quick Controls 2呢  https://github.com/qt/qtquickcontrols2/