您的位置:首页 > 编程语言 > C语言/C++

(虚)继承类的内存大小计算

2014-03-31 22:44 232 查看
今天接触到了虚继承类的大小计算,以前不是很清楚,所以刚开始没想出来,然后百度了一下发现网上各种的博客,然后选了几篇看了下。感觉举的例子大体都是一样的,但是有些地方个人感觉说的不是让人很明白,有点难理解,所以自己重新整理了下,以后忘了可以过来复习一下...

虚函数的工作原理涉及到了虚函数表指针vptr和虚函数表vtbl,当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的 vptr 指向的 vtbl,然后在 vtbl 中寻找合适的函数指针。如果类定义了虚函数,该类及其派生类就要生成一张虚拟函数表,即vtable。而在类的对象地址空间中存储一个该虚表的入口,占4个字节,这个入口地址是在构造对象时由编译器写入的。所以,由于对象的内存空间包含了虚表入口,编译器能够由这个入口找到恰当的虚函数,这个函数的地址不再由数据类型决定了。因为包含了虚函数的入口(4字节),个人感觉就是个指针。所以我们在计算类的大小的时候就要将其包含在内。

首先来一个不同的类继承的例子,也是众多博主举的例子

class A
{
};

class B
{
char ch;
virtual void func0()  {  }
};

class C
{
char ch1;
char ch2;
virtual void func()  {  }
virtual void func1()  {  }
};

class D: public A,  public C
{
int d;
virtual void func()  {  }
virtual void func1()  {  }
};

class E: public B, public C
{
int e;
virtual void func0()  {  }
virtual void func1()  {  }
};

int main(void)
{
cout<<"A="<<sizeof(A)<<endl;    //result=1
cout<<"B="<<sizeof(B)<<endl;    //result=8
cout<<"C="<<sizeof(C)<<endl;    //result=8
cout<<"D="<<sizeof(D)<<endl;    //result=12
cout<<"E="<<sizeof(E)<<endl;    //result=20
return 0;
}
A大小为1,因为空的类大小默认为1,B的大小为8,char占1字节,补齐之后是4,再加上虚函数表指针。C虽然有两个虚函数,但是只是在虚函数表中添加了一项,虚函数表指针还是1个,所以考虑内存补齐之后大小还为8。E的大小了,因为是普通继承,对于每个对象只有一个VPTR,换句话说他们共享了虚函数表指针,注意这个共享是怎么共享的,我理解的是B和C的虚函数表指针仍然存在,但是E没有了,所以这边E的大小是B+C+int变量的大小为20。

下面是虚继承

class CommonBase
{
int co;
};

class Base1: virtual public CommonBase
{
public:
virtual void print1() {  }
virtual void print2() {  }
private:
int b1;
};

class Base2: virtual public CommonBase
{
public:
virtual void dump1() {  }
virtual void dump2() {  }
private:
int b2;
};

class Derived: public Base1, public Base2
{
public:
void print2() {  }
void dump2() {  }
private:
int d;
};
size(Derived)大小为32,套用各位博主的分析图
class Derived size(32):
+---
| +--- (base class Base1)
| | {vfptr}
| | {vbptr}
| | b1
| +---
| +--- (base class Base2)
| | {vfptr}
| | {vbptr}
| | b2
| +---
| d
+---
+--- (virtual base CommonBase)
| co
+---


好了 ,以上是我根据网上资料学习到的

然后我自己修改了下例1中的代码

class A
{
};

class B
{
char ch;
virtual void func0()  {  }
};

class C
{
char ch1;
char ch2;
virtual void func()  {  }
virtual void func1()  {  }
};

class D: public A, public C
{
int d;
virtual void func()  {  }
virtual void func1()  {  }
};

class E:virtual public B, virtual public C
{
int e;
virtual void func0()  {  }
virtual void func1()  {  }
};

int main(void)
{
cout<<"A="<<sizeof(A)<<endl;    //result=1
cout<<"B="<<sizeof(B)<<endl;    //result=8
cout<<"C="<<sizeof(C)<<endl;    //result=8
cout<<"D="<<sizeof(D)<<endl;    //result=12
cout<<"E="<<sizeof(E)<<endl;    //result=24
return 0;
}


将E修改为了虚继承,然后我计算出来了E的大小为32,结果电脑跑出来为24,原来我还是没搞明白,然后通过各种调试我大概又知道了,首先不管是多重虚继承还是单一虚继承,指向虚基的指针(注意这边是虚基)只有一个,然后我注意到了E中定义的虚函数,发现其实在B和C中已经定义了,所以E里面没有虚函数表指针,综合这两点,E的大小就是int变量的大小+指向虚基的指针+B的大小+C的大小=24,为了证实我第二点的想法,我将E中虚函数func1修改未func8,然后计算出E大小为28,应该算是验证了我的想法。

另外我回看了一下直接继承的例子,然后尝试修改了E中虚函数的名称,但是发现跟虚函数继承不一样,他没有变化,还是原来的值,我想这也验证了直接继承中虚函数表指针共享的情况.

以上是我的个人理解,如果各位觉得有错误的地方的话欢迎指正.





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐