天天看點

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

根據幾篇部落格C++對象模型,c++涉及繼承和虛繼承時的記憶體布局 ,C++ 對象的記憶體布局(上) 作了一些歸納和總結,留着備用吧。

讨論問題:

  • 單繼承記憶體布局?
  • 單繼承(虛繼承,virtual繼承)的布局?
  • 多繼承記憶體布局?
  • 虛繼承記憶體布局?

1、單繼承(非虛繼承)

先看單繼承的例子:

#include<iostream>
using namespace std;
class Parent
{
    public:
        int age;
        Parent():age(){}
        virtual void f(){cout << "Parent::f()" << endl;}
        virtual void g(){cout << "Parent::g()" << endl;}
};
class Child : public Parent
{
    public:
        int num;
        Child():num(){}
        virtual void f(){cout << "Child::f()" << endl;}
        virtual void h(){cout << "Child::h()" << endl;}
        virtual void gc(){cout << "Child::gc()" << endl;}
};
class GrandChild : public Child
{
    public:
        int gpa;
        GrandChild() : gpa(){}
        virtual void f(){cout << "GrandChild::f()" << endl;}
        virtual void ggc(){cout << "GrandChild::ggc()" << endl;}
        virtual void gc(){cout << "GrandChild::gc()" << endl;}
};
           

單繼承的記憶體分布:

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

單繼承(非虛繼承)基本上繼承父類的 vptr,并且會将自己的類的虛函數直接添加在子類的vptr,是一種 “is A ”的關系,可以共用vptr,而且對象成員變量也相應的繼承過來。

2、多繼承

using namespace std;
class B
{   public:
        int ib;
        char cb;
    public:
        B():ib(0),cb('B') {}
        virtual void f() { cout << "B::f()" << endl;}
        virtual void Bf() { cout << "B::Bf()" << endl;}
};
class B1 : virtual public B
{
    public:
        int ib1;
        char cb1;
    public:
        B1():ib1(11),cb1('1') {}
        virtual void f() { cout << "B1::f()" << endl;}
        virtual void f1() { cout << "B1::f1()" << endl;}
        virtual void Bf1() { cout << "B1::Bf1()" << endl;}
};
class B2: virtual public B
{
    public:
        int ib2;
        char cb2;
    public:
        B2():ib2(12),cb2('2') {}
        virtual void f() { cout << "B2::f()" << endl;}
        virtual void f2() { cout << "B2::f2()" << endl;}
        virtual void Bf2() { cout << "B2::Bf2()" << endl;}
};
class D : public B1, public B2
{
    public:
        int id;
        char cd;
    public:
        D():id(100),cd('D') {}
        virtual void f() { cout << "D::f()" << endl;}
        virtual void f1() { cout << "D::f1()" << endl;}
        virtual void f2() { cout << "D::f2()" << endl;}
        virtual void Df() { cout << "D::Df()" << endl;}
};
           

多繼承的對象分布:

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

多繼承也基本類似單繼承,隻不過多了幾張虛函數表,位置是根據多繼承聲明的順序。第一張虛表還安插自己類的虛函數。每個表後面就是繼承子類的成員變量。很明顯,如果多繼承的基類有共同的 “基基類”,必然就有2份“基基類”成員

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

3、虛繼承

如果上面的多繼承的基類(B1,B2)虛繼承B,那麼分布就是如下:

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

從圖上可以看出,多了額外的一張虛表 就是 “基基類”的虛表,虛繼承是一種“has A”的關系,不是is-a關系,會儲存自己的一張虛表和成員,後續派生的資料成員也是隻有一份。

注意:VS上,在B1和B2的 vptr和成員之間位置,會添加虛基類指針vbptr(可能是偏移位址或者直接位址),而clang和GCC沒有,其他編譯器實作不清楚,有些編譯器可能将虛基類的偏移位址 安插到虛函數表指針中(正偏移為虛函數,負為虛基類)

虛繼承B1和B2的對象分布

C++的繼承,多繼承,虛繼承的對象分布的總結1、單繼承(非虛繼承)2、多繼承3、虛繼承

這裡對于虛線框處,inside object C++ model 說對于虛基類會有一個 基類表指針,指向虛基類的偏移或者虛基類的位址,這裡本人在實驗的時候,并沒有發現 vbptr,vs似乎會生成在vptr和成員變量之間。

棕色部分根據編譯器實作而定,vs上似乎會生成(放在vptr和成員變量之間),gcc和clang沒有這部分。

注意:VS上,在B1和B2的 vptr和成員之間位置,會添加虛基類指針vbptr(可能是偏移位址或者直接位址),而clang和GCC沒有,其他編譯器實作不清楚,有些編譯器可能将虛基類的偏移位址 安插到虛函數表指針中(正偏移為虛函數,負為虛基類)

測試代碼:

機器在64位:指針為8bytes,而且按8bytes對齊方式

#include <iostream>
using  namespace std;
class Base{
public:
    virtual void f(){cout<<"Base::f()";}
    virtual void g(){cout<<"Base::g()";}
    virtual void h(){cout<<"Base::h()";}
    long b=;
};
class Base2:virtual Base{
public:
    virtual void f(){cout<<"Base2::f()";}
    virtual void g(){cout<<"Base2::g()";}
    virtual void gb2(){cout<<"Base2::gb2()";}
    long b=;
};
class Base3:virtual Base{
public:
    virtual void f(){cout<<"Base3::f()";}
    virtual void g(){cout<<"Base3::g()";}
    virtual void gb3(){cout<<"Base3::gb3()";}
    long b=;
};

class Derive:public Base2,public Base3{
public:
    virtual void f(){cout<<"Derive::f()";}
    virtual void g(){cout<<"Derive::g()";}
    virtual void gb4(){cout<<"Derive::gb4()";};
    long b=;
};
int main() {
    //shit  這裡是有8位元組對齊,int類型存放也是8位元組對齊
/*    using  Fun= void (*)();
    Fun fun= nullptr;
    Derive d;
    cout<< "虛函數表位址:"<<(long *)&d<<endl;
    cout<<"虛表-第一個函數位址:"<<(long*)*(long*)&d<<endl;
    //int* b=(int*)((long*)&d+3);  //b 位址(如果是int會進行補齊)
    long*b=(long*)&d+1;
    cout<<*b<<" "<<*(b+1)<<endl;
    long **b2=(long**)((long*)&d+2);//函數表位址
    fun=(Fun)b2[0][2];
    fun();*/
    using  Fun= void (*)();
    Fun fun= nullptr;
    Base2 b2;
    long*b=(long*)&b2+;
    //cout<<*b<<" "<<*(b+1)<<endl;
    long** f=(long**)((long*)&b2 + );
    cout<< sizeof(b2);
    return ;
}
           

繼續閱讀