virtual 继承的一个类的sizeof()大小变化

对于,class A.

  
classM;
class B:virtual A.

sizeof(B) == 4

  
class C:virtual A 
class D: B ,C
zsieof(D) == 8
class E :virtual M
class D:virtual B,C,M
sizeof(D) == 16 virtual 的继承 都干了沙事情亚

我看了下各位的回答,但是我就对于地下代码很好奇

  
calss A
{
};
class K
{
};

class B:virtual public A
{
};

class C:virtual public A
{
};

class H:virtual public K
{
};

class F:virtual public K
{
};

class D:virtual public B
{
}; sizeof(D) == 8

class D:virtual public B ,public C
{
}; sizeof(D) == 8 这里为啥

class D:virtual public B ,public C,public E
{
}; sizeof(D) == 20 这里为啥

class D:virtual public B ,public C,public E,public F
{
}; sizeof(D) == 20 这里为啥

class D:virtual public B ,public C,public E,public F,public A
{
}; sizeof(D) == 24 这里应该是内存其

class D:virtual public B ,public C,public E,public F,public A,public K
{
}; sizeof(D) == 24 这里应该是内存其

从调试中 看不到 D对象里面有啥东西呀,但是的空间变化是如何变得

linux_ubuntu
linux_ubuntu
1679
编辑于2012-03-05
评论 (0)链接2012-02-29 

搞清楚这个问题可以去看一下Inside The C++ Object Model,不过里面也有一些错的地方,理解一下就好了,你看到的结果在不同编译器下结果是不同的,标准里只有功能描述,对内存布局并没有严格要求。

上代码,自己运行着分析一下,代码很久了,有可能有不对的地方,自己理理,sizeof的结果也自行分析吧。

  
#ifndef OM_SINGLE_VIRTUAL_INHERITANCE_H_
#define OM_SINGLE_VIRTUAL_INHERITANCE_H_

// 单继承模式下,虚拟继承
// 若子类没有新定义virtual函数 此时子类的布局是 :
// 低地址 -> 高地址
// vbptr,子类的元素, 虚基类的元素.
// 在虚基类派生出来的类中,虚基类的位置一般情况下在存储区的尾部,需要一个中介才能访问虚基类的对象
// 所以虽然没有virtual函数,子类也需要有一个vbptr

#include "om-port.h"
//////////////////////////////////////////////////////////////////////////
// 测试单一继承体系下的内存布局情况(虚拟继承)
namespace om_ns_svi {

class CParent {
public:
CParent ():iparent_ (10) {}
int iparent_;
};

class CChild_a : virtual public CParent {
public:
CChild_a():ichild_(100) {}
int ichild_;
};

class CChild_b : virtual public CParent {
public:
CChild_b():ichild_(100) {}
virtual void f(){ cout<<"CChild_b::f()"<<endl;}
int ichild_;
};

class CParent_vf {
public:
CParent_vf ():iparent_ (10) {}
virtual void f() { cout << "CParent_vf::f()" << endl;}
virtual void g() { cout << "CParent_vf::g()" << endl;}
int iparent_;
};

class CChild_c : virtual public CParent_vf {
public:
CChild_c():ichild_(100) {}
virtual void f(){ cout << "CChild_c::f()" << endl;}
virtual void h(){ cout << "CChild_c::h()" << endl;}
int ichild_;
};

inline void TestSingleVirtualInheritance() {
#pragma warning(push)
#pragma warning(disable:4312 4311)

int i = 0;
Ptr_Void_Fun pFun = NULL;
int** pVTable = NULL;
cout << " Test Single Virtual Inheritance "<<endl;
cout << "***************************************************************************" <<endl;
cout << endl << "--------------- Test CChild_a" << endl;
CChild_a cda;
pVTable = (int**)&cda;
cout << "size: " << sizeof(cda) << endl; // 有无虚函数都会有一个基类虚表指针
cout << (int*)&pVTable[0] << "\t" << "[0] CChild_a::vbptr" << endl;
cout << "\t\t\t" << "[0] CChild_a::vbptr for CChild_a (" << &pVTable[0][0] << ") = " <<pVTable[0][0]<< endl;
cout << "\t\t\t" << "[1] CChild_a::vbptr for CParent (" << &pVTable[0][2] << ") = " <<pVTable[0][3]<< endl;

cout << (int*)&pVTable[1] << "\t" << "[1] CChild_a.ichild_ = " << (int)pVTable[1] << endl;
cout << (int*)&pVTable[2] << "\t" << "[2] CParent.iparent_ = " << (int)pVTable[2] << endl;

cout << endl << "--------------- Test CChild_b" << endl;
CChild_b cdb;
pVTable = (int**)&cdb;
cout << "size: " << sizeof(cdb) << endl;
// 在GCC下,这种情况下的虚表最后一个入口不是一个函数
cout << (int*)&pVTable[0] << "\t" << "[0] CChild_b::vfptr->" << endl;
#if defined(OM_COMPILER_MSVC_)
for (i = 0; (Ptr_Void_Fun)pVTable[0][i] != NULL; i++) {
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[0][i]<<"\t";
pFun = (Ptr_Void_Fun)pVTable[0][i];
pFun();
}
#else
for (i = 0; (Ptr_Void_Fun)pVTable[0][i+1] != NULL; i++) {
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[0][i]<<"\t";
pFun = (Ptr_Void_Fun)pVTable[0][i];
pFun();
}
cout << "\t\t\t["<<i<<"] " << "\t" << &pVTable[0][i] << "\t" <<(int)pVTable[0][i] << endl;;
#endif

#if defined(OM_COMPILER_MSVC_)
cout << (int*)&pVTable[1] << "\t" << "[1] CChild_b::vbptr" << endl;
cout << "\t\t\t" << "[0] CChild_b::vbptr for CChild_b (" << &pVTable[1][0] << ") = " <<pVTable[1][0]<< endl;
cout << "\t\t\t" << "[1] CChild_b::vbptr for CParent (" << &pVTable[1][4] << ") = " <<pVTable[1][5]<< endl;

cout << (int*)&pVTable[2] << "\t" << "[2] CChild_b.ichild_ = " << (int)pVTable[2] << endl;
cout << (int*)&pVTable[3] << "\t" << "[3] CParent.iparent_ = " << (int)pVTable[3] << endl;
#else
cout << (int*)&pVTable[1] << "\t" << "[1] CChild_b.ichild_ = " << (int)pVTable[1] << endl;
cout << (int*)&pVTable[2] << "\t" << "[2] CParent.iparent_ = " << (int)pVTable[2] << endl;
#endif

cout << endl << "--------------- Test CParent_vf" << endl;
CParent_vf cpvf;
pVTable = (int**)&cpvf;
cout << "size: " << sizeof(cpvf) << endl;
cout << (int*)&pVTable[0] << "\t" <<"[0] CParent::_vptr->" << endl;

#if defined(OM_COMPILER_MSVC_)
for (i = 0; (Ptr_Void_Fun)pVTable[0][i] != NULL; i++) {
pFun = (Ptr_Void_Fun)pVTable[0][i];
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[0][i]<<"\t";
pFun();
}
#else
for (i = 0; (Ptr_Void_Fun)pVTable[0][i+1] != NULL; i++) {
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[0][i]<<"\t";
pFun = (Ptr_Void_Fun)pVTable[0][i];
pFun();
}
cout << "\t\t\t["<<i<<"] " << "\t" << &pVTable[0][i] << "\t" <<(int)pVTable[0][i] << endl;;
#endif

cout << (int*)&pVTable[1] << "\t" << "[1] CParent.iparent_ = " << (int)pVTable[1] << endl;

cout << endl << "--------------- Test CChild_c" << endl;
CChild_c cdc;
pVTable = (int**)&cdc;
cout << "size: " << sizeof(cdc) << endl; // 有无虚函数都会有一个基类虚表指针
cout << (int*)&pVTable[0] << "\t" << "[0] CChild_c::vfptr" << endl;

for (int i = 0; (Ptr_Void_Fun)pVTable[0][i] != NULL; i++) {
pFun = (Ptr_Void_Fun)pVTable[0][i];
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[0][i]<<"\t";
pFun();
}

#if defined(OM_COMPILER_MSVC_)
cout << (int*)&pVTable[1] << "\t" << "[1] CChild_c::vbptr" << endl;
cout << "\t\t\t" << "[0] CChild_c::vbptr for CChild_b (" << &pVTable[1][0] << ") = " <<pVTable[1][0]<< endl;
cout << "\t\t\t" << "[1] CChild_c::vbptr for CParent_vf (" << &pVTable[1][6] << ") = " <<pVTable[1][7]<< endl;

cout << (int*)&pVTable[2] << "\t" << "[2] CChild_c.ichild_ = " << (int)pVTable[2] << endl;
cout << (int*)&pVTable[3] << "\t" << "[3] CParent_vf::vfptr->" << endl;
for (int i = 0; (Ptr_Void_Fun)pVTable[3][i] != NULL; i++) {
pFun = (Ptr_Void_Fun)pVTable[3][i];
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[3][i]<<"\t";
pFun();
}
cout << (int*)&pVTable[4] << "\t" << "[4] CParent.iparent_ = " << (int)pVTable[4] << endl;
#else
cout << (int*)&pVTable[1] << "\t" << "[1] CChild_c.ichild_ = " << (int)pVTable[1] << endl;
cout << (int*)&pVTable[2] << "\t" << "[2] CParent_vf::vfptr->" << endl;
for (int i = 0; (Ptr_Void_Fun)pVTable[2][i] != NULL; i++) {
pFun = (Ptr_Void_Fun)pVTable[2][i];
cout << "\t\t\t["<<i<<"] " << "\t" << (int*)pVTable[2][i]<<"\t";
pFun();
}
cout << (int*)&pVTable[3] << "\t" << "[3] CParent.iparent_ = " << (int)pVTable[3] << endl;
#endif
#pragma warning(pop)
cout << endl;
}
}//om_ns_svi

#endif

VC8.0下:

  
Test Single Virtual Inheritance                     





--------------- Test CChild_a
size: 12
0012FE60 [0] CChild_a::vbptr
[0] CChild_a::vbptr for CChild_a (0041FBAC) = 0
[1] CChild_a::vbptr for CParent (0041FBB0) = 8
0012FE64 [1] CChild_a.ichild_ = 100
0012FE68 [2] CParent.iparent_ = 10

--------------- Test CChild_b
size: 16
0012FE48 [0] CChild_b::vfptr->
[0] 0041120D CChild_b::f()
0012FE4C [1] CChild_b::vbptr
[0] CChild_b::vbptr for CChild_b (0041FBC4) = -4
[1] CChild_b::vbptr for CParent (0041FBC8) = 8
0012FE50 [2] CChild_b.ichild_ = 100
0012FE54 [3] CParent.iparent_ = 10

--------------- Test CParent_vf
size: 8
0012FE38 [0] CParent::_vptr->
[0] 0041110E CParent_vf::f()
[1] 0041106E CParent_vf::g()
0012FE3C [1] CParent.iparent_ = 10

--------------- Test CChild_c
size: 20
0012FE1C [0] CChild_c::vfptr
[0] 00411492 CChild_c::h()
0012FE20 [1] CChild_c::vbptr
[0] CChild_c::vbptr for CChild_b (0041FC24) = -4
[1] CChild_c::vbptr for CParent_vf (0041FC28) = 8
0012FE24 [2] CChild_c.ichild_ = 100
0012FE28 [3] CParent_vf::vfptr->
[0] 00411069 CChild_c::f()
[1] 0041106E CParent_vf::g()
0012FE2C [4] CParent.iparent_ = 10

GCC下:

  
Test Single Virtual Inheritance




--------------- Test CChild_a
size: 12
0x22ff10 [0] CChild_a::vbptr
[0] CChild_a::vbptr for CChild_a (0x4781cc) = 0
[1] CChild_a::vbptr for CParent (0x4781d0) = 8
0x22ff14 [1] CChild_a.ichild_ = 100
0x22ff18 [2] CParent.iparent_ = 10
--------------- Test CChild_b
size: 12
0x22ff04 [0] CChild_b::vfptr->
[0] 0x4221f0 CChild_b::f()
[1] 0x4781e0 8
0x22ff08 [1] CChild_b.ichild_ = 100
0x22ff0c [2] CParent.iparent_ = 10
--------------- Test CParent_vf
size: 8
0x22fefc [0] CParent::_vptr->
[0] 0x421658 CParent_vf::f()
[1] 0x421684 CParent_vf::g()
[2] 0x4781c0 8
0x22ff00 [1] CParent.iparent_ = 10
--------------- Test CChild_c
size: 16
0x22feec [0] CChild_c::vfptr
[0] 0x422248 CChild_c::f()
[1] 0x422274 CChild_c::h()
0x22fef0 [1] CChild_c.ichild_ = 100
0x22fef4 [2] CParent_vf::vfptr->
[0] 0x46f128 CChild_c::f()
[1] 0x421684 CParent_vf::g()
0x22fef8 [3] CParent.iparent_ = 10

看你更新问题,本想更新代码,把各种情况贴一下让你自己分析的,不料字数有限制:源码下载
代码也参考了一些网上的资料,时间比较久了,如果出现什么错误,那就不是我写的。。。

在VC8.0和GCC下分别运行看看结果,找找规律,分析好了这几种情况,可以稍微深入的想一下,基于这样的内存模型可以想想如何实现的多态、方法的重载、覆盖、对数据的访问如何实现的、RTTI如何实现、虚析构函数等等问题,如果能想明白这些,内存模型基本上就没什么问题了,也就不用一直纠结于此类问题了。

王辉
编辑于 2012-03-01
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-02-29

单虚拟继承的派生对象会多出一个虚基类表指针vbptr,所以sizeof(class B:virtual A) = 4
@王辉的代码运行输出:

  
0012FE60    [0] **CChild_a::vbptr**
[0] CChild_a::vbptr for CChild_a (0041FBAC) = 0
[1] CChild_a::vbptr for CParent (0041FBB0) = 8

粗体部分就是占位4个字节的vbptr。
一般的继承所形成的对象内存布局都是子类对象直接包含父类对象,虚拟继承则通过vbptr来共享相同基类的父类对象。
请参考http://www.cnblogs.com/DylanWind/archive/2009/01/12/1373919.html

王辉
王辉
6753
编辑于 2012-02-29
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-02-29

对于查看类的对象的内存布局,微软内部在VC2005中(要先进入Microsoft Visual Studio -> Visual Studio Tools -> Visual Studio 2005命令提示)提供了一个非常重要的编译选项:/d1reportSingleClassLayout.
或者在命令行下:
cl 源文件名 /d1reportSingleClassLayoutChild

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-03-04

看看C++对象模型那本书上对继承和虚函数的解释吧

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-03-04

把你描述的问题写成代码如下(具体分析在代码中):

  
#include<iostream>
using namespace std;

class A
{
};

class M
{
};

class B:virtual public A
{
};

class C:virtual public A
{
};

class D:public B ,public C
{
};

class E:virtual public M
{
};

class F:virtual public B,public C,public M
{
};

int main()
{
//B里面存了一个指向虚基表的指针,大小为4bytes
cout<<"sizeof(B)="<<sizeof(B)<<endl;

//D包含了B、C,故大小为8bytes
cout<<"sizeof(D)="<<sizeof(D)<<endl;

//F虚拟继承于B,存了一个指向虚基表的指针,又一般继承于C和M,故大小为(4+4+0)bytes
cout<<"sizeof(F)="<<sizeof(F)<<endl;

return 0;
}

结果如下(编译器为GCC):

请输入图片描述

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-12-08

你可以用编译器看看。继承之后,子类会包含父类.

sizeof(M) = 1,实例化的问题,每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-02-29

有虚函数的类里维护了一个虚表

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-09-06
德问是一个专业的编程问答社区,请 登录注册 后再提交答案