c++ 虚继承详解

来源:互联网 发布:java native static 编辑:程序博客网 时间:2024/05/14 21:28

#include <iostream>
using namespace std;
class A
{
 virtual void f(){};
};
class B : public virtual  A{
  
 virtual void f(){};
};
class C: public virtual  A{
 virtual void f(){};
 virtual void t(){};
};
int main() 

   cout<<sizeof(B)<<endl;
  cout<<sizeof(C)<<endl;

此题在vc6.0下结果是 8 12。

回顾一下虚继承的概念:虚继承主要解决在多重继承中的菱形继承问题,也就是说 B和C类同时继承了A类,然后D类继承了B,C类,那么D类的虚表就会有重复的函数指针,虚继承就不会了……。实现方法是,在子类中保存一个父类(虚继承类)的实体,同时保存一个指针指向这个实体。指针+实体都是属于子类,所以sizeof会将两者也算在内。

cout<<sizeof(B)<<endl; 结果是8原因是 sizeof(A)加上 指向A实体的指针。注意此时没有属于B的虚指针。也就是说B没有自己的虚函数。

 cout<<sizeof(C)<<endl; C B区别在于C中有一个属于自己的虚函数,所以加上了一个虚指针的大小 所有为12



最后我们来看一个完整的例子以及内存结构布局。图后有相关代码。

 

 VS2003虚继承内存结构图

 有了之前的那个思想,我来讲解下 各个结构体的 sizeof();

代码如下:

struct A

{

    A(int v=100):X(v){};

    virtual void foo(void){}

    int X;

};

 A:很简单 一个虚表指针 +一个 X 一共是8byte

struct B :virtual public A

{

    B(int v=10):Y(v),A(100){};

    virtual void fooB(void){}

    int Y;

};

 B:虚继承,OK 那就是 sizeof(A)+一个指向虚基类的指针4byte+判断B中的虚函数是不是从A继承的,如果是则这一部分是0,如果不是则还要再加4byte 存放 虚表 那么 B一共就是20byte。

struct C : virtual public A

{

    C(int v=20):Z(v),A(100){}

    virtual void fooC(void){}

    int Z;

};

 C的分析同A

 

struct D : public B, public C

{

    D(int v =40):B(10),C(20),A(100),L(v){}

    virtual void fooD(void){}

    int L;

};

 D:公共继承B,C,那么 直接 sizeof(b)+sizeof(C)+自己的一个虚指针-因为B,C都是虚继承A,那么B和C中关于A的指针只要保存一个,所以要减去4个字节,那么D最后一共就是40byte OK

 

int _tmain(int argc, _TCHAR* argv[])

{

   

    A a;

    int *ptr;

    ptr = (int*)&a;

    cout << ptr << " sizeof = " << sizeof(a) <<endl;

    for(int i=0;i<sizeof(A)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

 

    cout << "--------------------------------------" <<endl;

 

    B b;

    ptr = (int*)&b;

    cout <<"addr:" << ptr << " sizeof = " << sizeof(b) <<endl;

    for(int i=0;i<sizeof(B)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

 

    cout << "--------------------------------------" <<endl;

   

    D d;

    ptr = (int*)&d;

    cout <<"addr:" << ptr << " sizeof = " << sizeof(d) <<endl;

    for(int i=0;i<sizeof(D)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

    return 0;

}


最后一段话很重要:那就是 各个编译器运行的关于虚继承的结果不一样,很简单,他们处理虚表的机制不一样,但是有一点可以肯定的是,虚继承就是为了解决菱形继承中,B,C都继承了A,D继承了B,C,那么D关于 A的引用只有一次,而不是 普通继承的 对于A引用了两次……

所以上面分析的都是浮云 没用的东西