虚继承下类的大小

文章摘自  https://www.cnblogs.com/yvictoryr/p/3773733.html  ,红色字体为我认为原博客错误的地方。

 

虚继承是为了解决多重继承中的问题而出现的。要理解虚继承的实现机制。

首先看一般继承:

复制代码

class A
{
    char k[3];
public:
    virtual void aa()
    {

    };
};
class B:public  A
{
    char j[3];
public:
    virtual void bb()
    {
        
    };
};
class C:public  B
{
    char i[3];
public:
    virtual void cc()
    {
        
    };
};
int main()
{
    cout<<sizeof(A)<<endl;
    cout<<sizeof(B)<<endl;
    cout<<sizeof(C)<<endl;  
    return 0;
}

复制代码

得到的答案是:8 12 16

分析如下:

1、  对于类A,有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量char k[3],则根据数据对齐原则,可以得到sizeof(A)的大小为8;

2、   对于类B,也有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量j[3],但是类B是继承自类A的,是一般继承,不是虚继承,所以可以得到sizeof(B)的大小为:  4(指向虚函数的虚指针)+4(自己的数据成员)+4(父类A的数据成员)=12;

3、  对于类C,也有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量i[3],同样的,类C是继承自类B的,是一般继承,不是虚继承,所以可以得到sizeof(C)的大小为4(指向虚函数的虚指针)+4(自己的数据成员)+8(父类的数据成员)=16;

以上表格的虚函数指针应该位于每个类的起始地址,而不是最后。

   

再来看一下虚继承的情况:

复制代码

class A
{
    char k[3];
public:
    virtual void aa()
    {

    };
};
class B:public virtual A
{
    char j[3];
public:
    virtual void bb()
    {
        
    };
};
class C:public virtual B
{
    char i[3];
public:
    virtual void cc()
    {
        
    };
};
int main()
{    
    cout<<sizeof(A)<<endl;    
    cout<<sizeof(B)<<endl;
    cout<<sizeof(C)<<endl;    
    return 0;
}

复制代码

得到的答案是:8 20 32

分析如下:

1、对于类A,有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量k[3],则根据数据对齐原则,可以得到sizeof(A)的大小为8;

2、对于类B,也有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量j[3],但是类B是继承自类A的,而且是虚继承,虚继承的实现是通过一个虚基类指针列表,比如vptr_B_A指向虚基类,同时还包含了父类的所有内容,所以可以得到sizeof(B)的大小为:4(指向自己虚函数的虚指针)+4(自己的数据成员)+4(指向虚基类的指针vptr_B_A)+8(父类A的内容大小)=20;

3、对于类C,也有一个虚函数,需要一个虚函数表vtbl来记录函数的入口地址,每个地址一个虚指针,指针的大小为4,类中还有一个成员变量i[3],同样的,类C是继承自类B的,而且是虚继承,虚继承的实现是通过一个虚基类指针列表,比如vptr_C_B指向虚基类,同时还包含了父类的所有内容,所以可以得到sizeof(C)的大小为:4(指向自己虚函数的虚指针)+4(自己的数据成员)+4(指向虚基类的指针(vptr_C_B)+20(父类B的内容大小)=32;

复制代码

class A
{
    char k[3];
public:
    virtual void aa()
    {

    };
};
class B:public virtual A
{
    char j[3];
public:
    virtual void bb()
    {
        
    };
};
class C:public virtual A
{
    char i[3];
public:
    virtual void cc()
    {
        
    };
};
class D: public B,public C
{
    char n[3];
    public:
        virtual void dd()
        {
        
         };
};
int main()
{    
    cout<<sizeof(A)<<endl;    
    cout<<sizeof(B)<<endl;
    cout<<sizeof(C)<<endl; 
    cout<<sizeof(D)<<endl;   
    return 0;
}

复制代码

得到的结果为:8 20 20 36

分析如下:

1、类sizeof(A,B,C),从前面的分析可以得到结果为8 20 20;

2、sizeof(D) = 

12【父类B的内容,包括指向虚函数的虚指针(4,合并类D的虚函数)、虚基类指针列表vptr_B_A(4)、数据成员(4)】

+ 12【父类C的内容,包括指向虚函数的虚指针(4)、虚基类指针列表vptr_C_A(4)、数据成员(4)】

+ 4(类D的的数据成员)+ 4(类A虚函数的虚指针)+ 4(类A的数据成员)= 36。

     

可见,类D中只包含了类A的一份副本,虚继承很好地解决了多重继承中的二义性问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值