コンパイラの補助情報からc++オブジェクトメモリレイアウトを見る

20701 ワード

  • プログラミング
  • cpp

  • 事前知識
    本稿では,32ビットのコンパイラを用いてコンパイルした結果,クラスのメモリレイアウト情報を印刷できる.
    DevCPP IDE
    このIDEは私の好きなwindowsの下のcppのIDEの1つで、それはツール->コンパイルオプションがあって、コンパイラのタイプを選択することができて、コンパイルオプションの中でいくつかの情報を加えることができて、メモリのレイアウトの情報を出力することができるため、私はコンパイルの時に以下のコマンドに参加します
    --std=c++11  -fdump-class-hierarchy 
    

    -fdump-class-hierarchyこのオプションはgccコンパイル時にクラスのレイアウト情報を生成することができ、生成されたファイル名は6に類似する.cpp.002t.class
    vs2017
    vsで使用されるコンパイラはclです.コマンドは/d 1 reportAllClassLayoutです.すべてのクラス関連レイアウトを出力します.
    clang
    clang -Xclang -fdump-record-layouts
    メモリレイアウト情報の理解
    テストコードは次のとおりです.
    class father{
        int a;
        int b;
    };
    
    class child: public father{
    };
    
    int main(){
        return 0;
    }
    

    g++-fdump-class-hierarchy testを用いる.cppはtestを生成する.cpp.002t.classの内容は以下の通りです
    Class father
       size=8 align=4
       base size=8 base align=4
    father (0x0x16662d8) 0
    
    Class child
       size=8 align=4
       base size=8 base align=4
    child (0x0x3984e00) 0
      father (0x0x1666310) 0
    

    クラスのサイズと配置情報が表示されます.
    标题:メモリの位置合わせを理解する
  • 構造体の最初のメンバーのオフセット量(offset)は0であり、その後、各メンバーの構造体ヘッダアドレスに対するoffsetは、必要に応じてコンパイラがメンバー間にバイトを埋め込むように、有効な位置合わせ値より小さいメンバーの整数倍である.
  • 構造体の合計サイズは、有効な整列値の整数倍であり、必要に応じてコンパイラは最後のメンバーの後にパディングバイトを加算します.

  • マルチステートとダミーテーブル
    マルチステートとは、単純に言えば、継承階層で親のポインタが複数の形態を有することを意味します.親の関数ではなく、子のオブジェクトを指す場合、子の関数を呼び出すことができます.
    ダミー関数ポインタは、通常、オブジェクトメモリレイアウトの最初の位置に配置されます.これは、多層継承または多重継承の場合に、ダミー関数テーブルを最も効率的に取得できるようにするためです.vprtがオブジェクトメモリの一番前にある場合、オブジェクトのアドレスは虚関数ポインタアドレスです.虚関数ポインタのアドレスを取得できます.
    以下のように虚関数ポインタのアドレス(虚関数ポインタが指すアドレスではなく)を取得する
    Base b(1000);
    int * vptrAdree = (int *)(&b);
    

    vptrAdreeは虚関数ポインタ(虚関数テーブルを指す)を指し、
    最初の虚関数のアドレスは次のように得ることができます
    Base b(1000);
    using fun=void(*) ();
    fun fun1 = (fun)*((int *)*(int *)(&b));
    fun fun2 = (fun)*((int *)*(int *)(&c)+1);
    

    単純な場合、単層継承と虚関数の全オーバーライド
    コンパイラによって生成されたメモリレイアウト情報を参照してください.
    #include 
    using namespace std;
    
    class Base{
        int a;
        virtual int print(){
            cout<

    g++コンパイル後に生成されるメモリレイアウト情報は:
    Vtable for Base
    Base::_ZTV4Base: 4u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI4Base)
    8     (int (*)(...))Base::print
    12    (int (*)(...))Base::print2
    
    Class Base
       size=8 align=4
       base size=8 base align=4
    Base (0x0x4e057a8) 0
        vptr=((& Base::_ZTV4Base) + 8u)
    
    Vtable for child
    child::_ZTV5child: 4u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI5child)
    8     (int (*)(...))child::print
    12    (int (*)(...))child::print2
    
    Class child
       size=8 align=4
       base size=8 base align=4
    child (0x0x4e4b280) 0
        vptr=((& child::_ZTV5child) + 8u)
      Base (0x0x4e057e0) 0
          primary-for child (0x0x4e4b280)
    

    Vtableは虚関数テーブルを表しており、Vtable for Baseにはvptrが保存されており、位置はvptr=((&Base:_ZTV4 Base)+8 u)(ここでは8のオフセットが加算されていることに注意)であり、この位置の最初の内容は(int(*)(...)である.Base::print.
    複雑な場合:多重継承と一部の虚関数のオーバーライド
    #include 
    using namespace std;
    class Parent {
    public:
        int iparent;
        Parent ():iparent (10) {}
        virtual void g() { cout << " Parent::g()" << endl; }
        virtual void f() { cout << " Parent::f()" << endl; }
        virtual void h() { cout << " Parent::h()" << endl; }
     
    };
     
    class Child : public Parent {
    public:
        int ichild;
        Child():ichild(100) {}
        virtual void g_child() { cout << "Child::g_child()" << endl; }
        virtual void h_child() { cout << "Child::h_child()" << endl; }
        virtual void f() { cout << "Child::f()" << endl; }
    };
     
    class GrandChild : public Child{
    public:
        int igrandchild;
        GrandChild():igrandchild(1000) {}
        virtual void g_child() { cout << "GrandChild::g_child()" << endl; }
        virtual void f() { cout << "GrandChild::f()" << endl; }
        virtual void h_grandchild() { cout << "GrandChild::h_grandchild()" << endl; }
    };
    int main(){
        typedef void(*Fun)(void);
        GrandChild gc;   
        int** pVtab = (int**)&gc;
         
        cout << "[0] GrandChild::_vptr->" << endl;
        for (int i=0; (Fun)pVtab[0][i]!=NULL; i++){
            Fun pFun = (Fun)pVtab[0][i];
            cout << "    ["<Vtable for Parent
    Parent::_ZTV6Parent: 5u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI6Parent)
    8     (int (*)(...))Parent::g
    12    (int (*)(...))Parent::f
    16    (int (*)(...))Parent::h
    
    Class Parent
       size=8 align=4
       base size=8 base align=4
    Parent (0x0x4de37a8) 0
        vptr=((& Parent::_ZTV6Parent) + 8u)
    
    Vtable for Child
    Child::_ZTV5Child: 7u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI5Child)
    8     (int (*)(...))Parent::g
    12    (int (*)(...))Child::f
    16    (int (*)(...))Parent::h
    20    (int (*)(...))Child::g_child
    24    (int (*)(...))Child::h_child
    
    Class Child
       size=12 align=4
       base size=12 base align=4
    Child (0x0x4e2a5c0) 0
        vptr=((& Child::_ZTV5Child) + 8u)
      Parent (0x0x4de37e0) 0
          primary-for Child (0x0x4e2a5c0)
    
    Vtable for GrandChild
    GrandChild::_ZTV10GrandChild: 8u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI10GrandChild)
    8     (int (*)(...))Parent::g
    12    (int (*)(...))GrandChild::f
    16    (int (*)(...))Parent::h
    20    (int (*)(...))GrandChild::g_child
    24    (int (*)(...))Child::h_child
    28    (int (*)(...))GrandChild::h_grandchild
    
    Class GrandChild
       size=16 align=4
       base size=16 base align=4
    GrandChild (0x0x4e2aa80) 0
        vptr=((& GrandChild::_ZTV10GrandChild) + 8u)
      Child (0x0x4e2aac0) 0
          primary-for GrandChild (0x0x4e2aa80)
        Parent (0x0x4de3818) 0
            primary-for Child (0x0x4e2aac0)
    
    

    調べてみると,子クラスの虚函数表の優先度は先親クラス,再子クラス再孫クラスであり,同一優先度は宣言順にアドレスを並べ,親クラスの虚関数が上書きされても元の位置に書くことで,親クラスのポインタが関数名でそのアドレスを見つけることが保証される.
    プログラムの実行結果は次のとおりです.
    [0] GrandChild::_vptr->
    
        [0] GrandChild::f()
        [1] Parent::g()
        [2] Parent::h()
        [3] GrandChild::g_child()
        [4] Child::h1()
        [5] GrandChild::h_grandchild()
    [1] Parent.iparent = 10
    [2] Child.ichild = 100
    [3] GrandChild.igrandchild = 1000
    

    わかる
  • テーブルは の にあります.
  • メンバー は、 および の に って に します.
  • の ではoverwriteの が テーブルで される.

  • #include 
    using namespace std;
    class Base1 {
    public:
        int ibase1;
        Base1():ibase1(10) {}
        virtual void f() { cout << "Base1::f()" << endl; }
        virtual void g() { cout << "Base1::g()" << endl; }
        virtual void h() { cout << "Base1::h()" << endl; }
     
    };
     
    class Base2 {
    public:
        int ibase2;
        Base2():ibase2(20) {}
        virtual void f() { cout << "Base2::f()" << endl; }
        virtual void g() { cout << "Base2::g()" << endl; }
        virtual void h() { cout << "Base2::h()" << endl; }
    };
     
    class Base3 {
    public:
        int ibase3;
        Base3():ibase3(30) {}
        virtual void f() { cout << "Base3::f()" << endl; }
        virtual void g() { cout << "Base3::g()" << endl; }
        virtual void h() { cout << "Base3::h()" << endl; }
    };
     
    class Derive : public Base1, public Base2, public Base3 {
    public:
        int iderive;
        Derive():iderive(100) {}
        virtual void f() { cout << "Derive::f()" << endl; }
        virtual void g1() { cout << "Derive::g1()" << endl; }
    };
    int main(){
        typedef void(*Fun)(void);
         
        Derive d;
         
        int** pVtab = (int**)&d;
         
        cout << "[0] Base1::_vptr->" << endl;
        Fun pFun = (Fun)pVtab[0][0];
        cout << "     [0] ";
        pFun();
         
        pFun = (Fun)pVtab[0][1];
        cout << "     [1] ";pFun();
         
        pFun = (Fun)pVtab[0][2];
        cout << "     [2] ";pFun();
         
        pFun = (Fun)pVtab[0][3];
        cout << "     [3] "; pFun();
         
        pFun = (Fun)pVtab[0][4];
        cout << "     [4] "; cout<"<"<Vtable for Base1
    Base1::_ZTV5Base1: 5u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI5Base1)
    8     (int (*)(...))Base1::f
    12    (int (*)(...))Base1::g
    16    (int (*)(...))Base1::h
    
    Class Base1
       size=8 align=4
       base size=8 base align=4
    Base1 (0x0x4d907a8) 0
        vptr=((& Base1::_ZTV5Base1) + 8u)
    
    Vtable for Base2
    Base2::_ZTV5Base2: 5u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI5Base2)
    8     (int (*)(...))Base2::f
    12    (int (*)(...))Base2::g
    16    (int (*)(...))Base2::h
    
    Class Base2
       size=8 align=4
       base size=8 base align=4
    Base2 (0x0x4d907e0) 0
        vptr=((& Base2::_ZTV5Base2) + 8u)
    
    Vtable for Base3
    Base3::_ZTV5Base3: 5u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI5Base3)
    8     (int (*)(...))Base3::f
    12    (int (*)(...))Base3::g
    16    (int (*)(...))Base3::h
    
    Class Base3
       size=8 align=4
       base size=8 base align=4
    Base3 (0x0x4d90818) 0
        vptr=((& Base3::_ZTV5Base3) + 8u)
    
    Vtable for Derive
    Derive::_ZTV6Derive: 16u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI6Derive)
    8     (int (*)(...))Derive::f
    12    (int (*)(...))Base1::g
    16    (int (*)(...))Base1::h
    20    (int (*)(...))Derive::g1
    24    (int (*)(...))-8
    28    (int (*)(...))(& _ZTI6Derive)
    32    (int (*)(...))Derive::_ZThn8_N6Derive1fEv
    36    (int (*)(...))Base2::g
    40    (int (*)(...))Base2::h
    44    (int (*)(...))-16
    48    (int (*)(...))(& _ZTI6Derive)
    52    (int (*)(...))Derive::_ZThn16_N6Derive1fEv
    56    (int (*)(...))Base3::g
    60    (int (*)(...))Base3::h
    
    Class Derive
       size=28 align=4
       base size=28 base align=4
    Derive (0x0x4ddeb40) 0
        vptr=((& Derive::_ZTV6Derive) + 8u)
      Base1 (0x0x4d90850) 0
          primary-for Derive (0x0x4ddeb40)
      Base2 (0x0x4d90888) 8
          vptr=((& Derive::_ZTV6Derive) + 32u)
      Base3 (0x0x4d908c0) 16
          vptr=((& Derive::_ZTV6Derive) + 52u)
    

    プログラムの は
    [0] Base1::_vptr->
         [0] Derive::f()
         [1] Base1::g()
         [2] Base1::h()
         [3] Derive::g1()
         [4] 1
    [1] Base1.ibase1 = 10
    [2] Base2::_vptr->
         [0] Derive::f()
         [1] Base2::g()
         [2] Base2::h()
         [3] 1
    [3] Base2.ibase2 = 20
    [4] Base3::_vptr->
         [0] Derive::f()
         [1] Base3::g()
         [2] Base3::h()
         [3] 0
    [5] Base3.ibase3 = 30
    [6] Derive.iderive = 100
    

    :
  • には の があります.
  • サブクラスのメンバー は、 の のテーブルに されます.
  • メモリレイアウトでは、 レイアウトが に べられています.
  • クラスの のf() はoverwriteによって クラスのf()になった.これは, なる クラスタイプのポインタが じサブクラスインスタンスを すことを するために, の を び すことができる.

  • #include 
    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 :  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:  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;}
     
    };
    int main(){
        typedef void(*Fun)(void);
        int** pVtab = NULL;
        Fun pFun = NULL;
         
        D d;
        pVtab = (int**)&d;
        cout << "[0] D::B1::_vptr->" << endl;
        pFun = (Fun)pVtab[0][0];
        cout << "     [0] ";    pFun();
        pFun = (Fun)pVtab[0][1];
        cout << "     [1] ";    pFun();
        pFun = (Fun)pVtab[0][2];
        cout << "     [2] ";    pFun();
        pFun = (Fun)pVtab[0][3];
        cout << "     [3] ";    pFun();
        pFun = (Fun)pVtab[0][4];
        cout << "     [4] ";    pFun();
        pFun = (Fun)pVtab[0][5];
        cout << "     [5] 0x" << pFun << endl;
         
        cout << "[1] B::ib = " << (int)pVtab[1] << endl;
        cout << "[2] B::cb = " << static_cast((int)(pVtab[2]))<< endl;
        cout << "[3] B1::ib1 = " << (int)pVtab[3] << endl;
        cout << "[4] B1::cb1 = " << (char)(int)pVtab[4] << endl;
         
        cout << "[5] D::B2::_vptr->" << endl;
        pFun = (Fun)pVtab[5][0];
        cout << "     [0] ";    pFun();
        pFun = (Fun)pVtab[5][1];
        cout << "     [1] ";    pFun();
        pFun = (Fun)pVtab[5][2];
        cout << "     [2] ";    pFun();
        pFun = (Fun)pVtab[5][3];
        cout << "     [3] ";    pFun();
        pFun = (Fun)pVtab[5][4];
        cout << "     [4] 0x" << pFun << endl;
         
        cout << "[6] B::ib = " << (int)pVtab[6] << endl;
        cout << "[7] B::cb = " << (char)(int)pVtab[7] << endl;
        cout << "[8] B2::ib2 = " << (int)pVtab[8] << endl;
        cout << "[9] B2::cb2 = " << (char)(int)pVtab[9] << endl;
         
        cout << "[10] D::id = " << (int)pVtab[10] << endl;
        cout << "[11] D::cd = " << (char)(int)pVtab[11] << endl;
        return 0; 
    } 
    
    

    メモリの は のとおりです.
    
    Vtable for B
    B::_ZTV1B: 4u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI1B)
    8     (int (*)(...))B::f
    12    (int (*)(...))B::Bf
    
    Class B
       size=12 align=4
       base size=9 base align=4
    B (0x0x4dc27a8) 0
        vptr=((& B::_ZTV1B) + 8u)
    
    Vtable for B1
    B1::_ZTV2B1: 6u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI2B1)
    8     (int (*)(...))B1::f
    12    (int (*)(...))B::Bf
    16    (int (*)(...))B1::f1
    20    (int (*)(...))B1::Bf1
    
    Class B1
       size=20 align=4
       base size=17 base align=4
    B1 (0x0x4e09780) 0
        vptr=((& B1::_ZTV2B1) + 8u)
      B (0x0x4dc27e0) 0
          primary-for B1 (0x0x4e09780)
    
    Vtable for B2
    B2::_ZTV2B2: 6u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI2B2)
    8     (int (*)(...))B2::f
    12    (int (*)(...))B::Bf
    16    (int (*)(...))B2::f2
    20    (int (*)(...))B2::Bf2
    
    Class B2
       size=20 align=4
       base size=17 base align=4
    B2 (0x0x4e09c40) 0
        vptr=((& B2::_ZTV2B2) + 8u)
      B (0x0x4dc2818) 0
          primary-for B2 (0x0x4e09c40)
    
    Vtable for D
    D::_ZTV1D: 14u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI1D)
    8     (int (*)(...))D::f
    12    (int (*)(...))B::Bf
    16    (int (*)(...))D::f1
    20    (int (*)(...))B1::Bf1
    24    (int (*)(...))D::f2
    28    (int (*)(...))D::Df
    32    (int (*)(...))-20
    36    (int (*)(...))(& _ZTI1D)
    40    (int (*)(...))D::_ZThn20_N1D1fEv
    44    (int (*)(...))B::Bf
    48    (int (*)(...))D::_ZThn20_N1D2f2Ev
    52    (int (*)(...))B2::Bf2
    
    Class D
       size=48 align=4
       base size=45 base align=4
    D (0x0x4e27040) 0
        vptr=((& D::_ZTV1D) + 8u)
      B1 (0x0x4e27080) 0
          primary-for D (0x0x4e27040)
        B (0x0x4dc2850) 0
            primary-for B1 (0x0x4e27080)
      B2 (0x0x4e270c0) 20
          vptr=((& D::_ZTV1D) + 40u)
        B (0x0x4dc2888) 20
            primary-for B2 (0x0x4e270c0)
    
    

    [0] D::B1::_vptr->
         [0] D::f()
         [1] B::Bf()
         [2] D::f1()
         [3] B1::Bf1()
         [4] D::f2()
         [5] 0x1
    [1] B::ib = 0
    [2] B::cb = B
    [3] B1::ib1 = 11
    [4] B1::cb1 = 1
    [5] D::B2::_vptr->
         [0] D::f()
         [1] B::Bf()
         [2] D::f2()
         [3] B2::Bf2()
         [4] 0x0
    [6] B::ib = 0
    [7] B::cb = B
    [8] B2::ib2 = 12
    [9] B2::cb2 = 2
    [10] D::id = 100
    [11] D::cd = D
    

    :
    
    

    の クラスBは,そのメンバ と がB 1とB 2に し,Dに されることが かった. 、DにはB 1とB 2のインスタンスがあり、BのメンバーはDのインスタンスに2つ し、1つはB 1が し、もう1つはB 2が したため、メモリが される.
    び し を に すことで を することができるが, の はまだ されていないので, によりDクラスにibエンティティを1つしか たせることができる.
    ダミー
    ダミー は、 で も したクラスが の インスタンスを つ を します. の クラスのメモリレイアウトは、 の とは きく なり、 に のようになります.
  • のサブクラス.それ が しい を している 、コンパイラは ポインタ(vptr)と テーブルを します.このvptrはオブジェクトメモリの にあります.vs : テーブルを します.
  • のサブクラスも、 のvprtと テーブルを に します.この は、サブクラスコンテンツと1つの4バイトの0で られます.
  • ダミー サブクラスオブジェクトには、4バイトのダミーテーブルポインタオフセット が まれます.

  • #include 
    using namespace std;
    class B
    {
    public:
        int ib;
    public:
        B(int i=1) :ib(i){}
        virtual void f() { cout << "B::f()" << endl; }
        virtual void Bf() { cout << "B::Bf()" << endl; }
    };
     
    class B1 : virtual public B
    {
    public:
        int ib1;
    public:
        B1(int i = 100 ) :ib1(i) {}
        virtual void f() { cout << "B1::f()" << endl; }
        virtual void f1() { cout << "B1::f1()" << endl; }
        virtual void Bf1() { cout << "B1::Bf1()" << endl; }
    };
    int main(){
        B1 b;
        using Fun=void(*)();
        Fun pFun = NULL;
        int ** pvtable = (int**)&b;
        for (int i=0; i<9; i++){
            if (pvtable[0][i]==NULL) {
                cout<

    メモリレイアウト
    Vtable for B
    B::_ZTV1B: 4u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI1B)
    8     (int (*)(...))B::f
    12    (int (*)(...))B::Bf
    
    Class B
       size=8 align=4
       base size=8 base align=4
    B (0x0x4f527a8) 0
        vptr=((& B::_ZTV1B) + 8u)
    
    Vtable for B1
    B1::_ZTV2B1: 12u entries
    0     8u
    4     (int (*)(...))0
    8     (int (*)(...))(& _ZTI2B1)
    12    (int (*)(...))B1::f
    16    (int (*)(...))B1::f1
    20    (int (*)(...))B1::Bf1
    24    0u
    28    4294967288u
    32    (int (*)(...))-8
    36    (int (*)(...))(& _ZTI2B1)
    40    (int (*)(...))B1::_ZTv0_n12_N2B11fEv
    44    (int (*)(...))B::Bf
    
    VTT for B1
    B1::_ZTT2B1: 2u entries
    0     ((& B1::_ZTV2B1) + 12u)
    4     ((& B1::_ZTV2B1) + 40u)
    
    Class B1
       size=16 align=4
       base size=8 base align=4
    B1 (0x0x4f9a540) 0
        vptridx=0u vptr=((& B1::_ZTV2B1) + 12u)
      B (0x0x4f527e0) 8 virtual
          vptridx=4u vbaseoffset=-12 vptr=((& B1::_ZTV2B1) + 40u)
    

    プログラム
    
    B1::f()
    B1::f1()
    B1::Bf1()
    here:0
    B::Bf()
    size == 16
    [1]:B1::ib1=100
    [3]:B::ib=1
    [2]:B::ib=4781096
    

    ここでのメモリ は、vc++の とは なる です.
    しかし、ここでsizeは16で、2つのポインタがあることを しています.
    マルチダミー
    の で, も したクラスDクラスのオブジェクトモデルはまた なる となっている.Dクラスオブジェクトのメモリ には、 の があります.
  • Dクラスオブジェクトメモリにおいて、ベースクラスが れる は、まずB 1( も の )、 にB 2( の の )、 にB( )
  • である.
  • DクラスオブジェクトのデータメンバーidはBクラスの に かれ、2つの のデータは として0で られている.
  • コンパイラは、Dクラスに のvptrを するのではなく、 も の の ベースクラステーブルを きして し、 に されたオブジェクトモデルと じです.
  • スーパークラスBの は、Dクラスオブジェクトのメモリレイアウトの に される.


  • C/C++メモリアライメントの
    C++オブジェクトのメモリレイアウト-coolshell
    C++オブジェクトモデル:オブジェクトメモリレイアウトの
    コンパイラはメモリレイアウトを します