虚拟继承的类对象的空间大小

  1. // 含有虚拟继承对象的空间大小.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include <iostream>
  5. using namespace std;
  6. class A{
  7. public:
  8. protected:
  9. private:
  10. };
  11. class B{
  12. public:
  13. protected:
  14. private:
  15. };
  16. class C:public A,public B{
  17. public:
  18. protected:
  19. private:
  20. };
  21. class D:virtual public A
  22. {
  23. public:
  24. protected:
  25. private:
  26. };
  27. class E:virtual public A,virtual public B{
  28. public:
  29. protected:
  30. private:
  31. };
  32. class F{
  33. public:
  34. int a;
  35. static int b;
  36. protected:
  37. private:
  38. };
  39. int F::b=10;
  40. int _tmain(int argc, _TCHAR* argv[])
  41. {
  42. cout<<"sizeof(A)="<<sizeof(A)<<endl;
  43. cout<<"sizeof(B)="<<sizeof(B)<<endl;
  44. cout<<"sizeof(C)="<<sizeof(C)<<endl;
  45. cout<<"sizeof(D)="<<sizeof(D)<<endl;
  46. cout<<"sizeof(E)="<<sizeof(E)<<endl;
  47. cout<<"sizeof(F)="<<sizeof(F)<<endl;
  48. getchar();
  49. return 0;
  50. }

由于A是空类,编译器会安插一个char,标记它的每一个对象,因此其大小为1个字节。

B类和A类一样,也是一个字节。

类C是多重继承自A和B,其大小仍然为1。

类D是虚继承自A,编译器为该类安插一个指向父类的指针,指针大小为4,由于此类有了指针,编译器不会安插一个char了,因此其大小为4字节。

类E虚继承自A并且也虚继承自B,因此它有指向父类A的指针与父类B的指针,加起来大小为8字节。

类F含有一个静态成员变量,这个静态成员的空间不在类的实例中,而是像全局变量一样在静态存储区,被类共享,因此其大小为4字节。

虚继承就是引入了虚基类的继承。引入虚基类的目的是为了解决类继承过程中产生的二义性问题。这种二义性问题常见于具有菱形继承关系的类继承体系中。比如: 有四个类:A、B、C、D,它们之间的继承关系是:B继承A,C继承A,D继承B和C。这就形成了一个菱形的继承关系,具有这种继承关系的图叫做有向无环 图。那么类D就有两条继承路径:D-->B-->A和D-->C-->A。而类A是派生类D的两条继承路径上的公共基类,那么这个公共基类就会在派生类D的对象中产生多个基类子对象,这个时候在D中引用基类A的成员时,就会产生明显的二义性。要解决这个二义性,就必须将这个基类A设定为虚基类。

引进虚基类之后,派生类(子类)的对象中只存在一个虚基类的子对象。当一个类拥有虚基类的时候,编译系统会为这个类的对象定义一个指针成员,并让它指向虚基类的子对象。该指针被称为虚基类指针。这个概念与虚函数表指针不同。在内存中,一般情况下,虚基类子对象在派生类对象中是放置在派生类对象所占内存块的尾部,不过这是由编译器来决定的。

C++虚继承内存对象模型探讨

[cpp] view
plain
copy

  1. class A {
  2. int a;
  3. virtual ~A(){}
  4. };
  5. class B:virtual public A{
  6. virtual ~B(){}
  7. virtual void myfunB(){}
  8. };
  9. class C:virtual public A{
  10. virtual ~C(){}
  11. virtual void myfunC(){}
  12. };
  13. class D:public B,public C{
  14. virtual ~D(){}
  15. virtual void myfunD(){}
  16. };

首先,说说GCC的编译器.

它实现比较简单,不管是否虚继承,GCC都是将虚表指针在整个继承关系中共享的,不共享的是指向虚基类的指针。

以上代码中 sizeof(A)=8,sizeof(B)=12,sizeof(C)=12,sizeof(D)=16.

解释:A中int+虚表指针。B,C中由于是虚继承因此大小为A+指向虚基类的指针,B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针。D由于B,C都是虚继承,因此D只包含一个A的副本,于是D大小就等于A+B中的指向虚基类的指针+C中的指向虚基类的指针。

如果B,C不是虚继承,而是普通继承的话,那么A,B,C的大小都是8(没有指向虚基类的指针了),而D由于不是虚继承,因此包含两个A副本,大小为16. 注意此时虽然D的大小和虚继承一样,但是内存布局却不同。

然后,来看看VC的编译器

vc对虚表指针的处理比GCC复杂,它根据是否为虚继承来判断是否在继承关系中共享虚表指针,而对指向虚基类的指针和GCC一样是不共享,当然也不可能共享。

代码同上。

运行结果将会是sizeof(A)=8,sizeof(B)=16,sizeof(C)=16,sizeof(D)=24.

解释:A中依然是int+虚表指针。B,C中由于是虚继承因此虚表指针不共享,由于B,C加入了自己的虚函数,所以B,C分别自己维护一个虚表指针,它指向自己的虚函数。(注意:只有子类有新的虚函数时,编译器才会在子类中添加虚表指针)因此B,C大小为A+自己的虚表指针+指向虚基类的指针。D由于B,C都是虚继承,因此D只包含一个A的副本,同时D是从B,C普通继承的,而不是虚继承的,因此没有自己的虚表指针。于是D大小就等于A+B的虚表指针+C的虚表指针+B中的指向虚基类的指针+C中的指向虚基类的指针。

同样,如果去掉虚继承,结果将和GCC结果一样,A,B,C都是8,D为16,原因就是VC的编译器对于非虚继承,父类和子类是共享虚表指针的。

时间: 2024-10-01 03:19:33

虚拟继承的类对象的空间大小的相关文章

面向对象--多继承&amp;派生类对象内存布局分析&amp;各基类指针所指向的位置分析

背景 原文链接:ordeder  http://blog.csdn.net/ordeder/article/details/25477363 关于非虚函数的成员函数的调用机制,可以参考: http://blog.csdn.net/yuanyirui/article/details/4594805 成员函数的调用涉及到面向对象语言的反射机制. 虚函数表机制可以查看下面这个blog: http://blog.csdn.net/haoel/article/details/1948051 总结为: 其一

c++类占内存空间大小计算

x64环境下实现: 1 #include <iostream> 2 3 using namespace std; 4 5 class kong{ 6 7 }; 8 9 class kong1{ 10 11 }; 12 13 class kong2{ 14 15 }; 16 17 class data2 18 { 19 int a; //4个字节 20 char s; 21 }; 22 23 class data3 24 { 25 int a; 26 char s; 27 virtual voi

Java面向对象一(封装 继承 多态 类 对象 方法)

1.封装:主要实现了隐藏细节,对用户提供访问接口,无需关心方法的具体实现. 如下,人这个类封装了很多人的属性和行为: 2.继承:很好的实现了代码的复用,提高了编程效率. 人{ 年龄(属性一) 身高(属性二) 性别(属性三) 做事(行为之一) 走路(行为之二) 说话(行为之三)} 教师{ 年龄(属性一) 身高(属性二) 性别(属性三) 做事(行为之一) 走路(行为之二) 说话(行为之三) 教书(行为之四)} 教师继承了人的属性和行为,额外多出的是教书的行为.所以可以写成 教师 继承了 人这个类 的

关于虚拟继承类的大小问题探索,VC++ 和 G++ 结果是有区别的

昨天笔试遇到个 关于类占用的空间大小的问题,以前没怎么重视,回来做个试验,还真发现了问题,以后各位笔试考官门,出题时请注明是用什么编译器. vc6/vc8 cl 和 Dev-C 的g++ 来做的测试: 上代码, 测试代码: #include <stdio.h>class A{public: int x;  int y; A() {  x = 1;     y = 2; }; void go() {  printf("A go()\n"); }    virtual void

C++中类的内存空间大小(sizeof)分析

首先明确各数据类型占多大的空间.例如int到底是占2字节还是4字节空间: 在TC里,int是2字节的(主要是因为TC是16位的,所以int类型也该是16位的)VC++里,int是4字节的,因为现代操作系统下的软件大多是是32位.64位的VC++,本来按理说,该是8字节的,但是可能为了维持32位的源代码移植到64位尽量不出错,所以也维持了4字节的长度.至于其他有名的编译器,如gcc,我还没用过,你得查一查它所规定int的长度 或者利用sizeof(int)也可以计算出来.本人电脑上计算如下: 在C

普通继承和虚拟继承的内存布局原则

环境: windows xp 3     VC2008 如果类A拥有虚函数,而类B普通继承自类A,那么一个类B的对象在内存布局里:类B的虚表会将类A虚表合并覆盖,然后先排列类A的数据,再排列类B的数据 如果类B虚拟继承自类A,那么一个类B的对象在内存布局里:类B的虚表和内容都不会将类A的合并,在类B的虚表之后插入一个虚基表,通过这个表来访问类A.

深入理解虚表之非虚拟继承及虚拟继承

非虚拟继承 [带虚函数的类] class Base { public: virtual void FunTest1() { cout<<"Base::FunTest1()"<<endl; } virtual void FunTest2() { cout<<"Base::FunTest2()"<<endl; } int _data1; }; int main() { Base b; b._data1 = 0x01; re

有关C++虚拟继承的简单理解

最近在看<深度探索C++对象模型>这本书的时候,里面第一章提到了虚拟继承,有这么一句话说:"在虚拟继承的情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实体."一开始我理解错了,以为这个继承类图体系里面全局只有一个base class对象,后来查了些资料才知道意思是一个子类中只包含一个bass class对象. 很多东西要有对比才能认识得更深刻,我们这里对比下虚拟继承和普通继承的子类对象的区别,也从而让我们更好地理解文章开头提到的书里的那句话. 虚

C++ 空类与多重继承的空类占用内存空间

1.输出以代码结果 #include <iostream> using namespace std; class A {}; class A2 {}; class B : public A {}; class C : public A, public A2 {}; class D : public virtual B {}; int main() { cout << sizeof(A) << endl; cout << sizeof(B) <<