オブジェクトの構造とダミーテーブル

6136 ワード

こんなクラスがあるとしたら
 1 class Base

 2 {

 3 public:

 4     Base()

 5     {

 6         clear();

 7     }

 8     virtual ~Base()

 9     {

10         int a = 0;

11     }

12     virtual void fun()

13     {

14         cout << "base" << endl;

15     }

16     void clear()

17     {

18         memset(this,0,sizeof Base);

19     }

20     int m_int32;

21 };

22 class child : public Base

23 {

24 public:

25     virtual ~child()

26     {

27         int a = 0;

28     }

29     virtual void fun()

30     {

31         cout << "child" << endl;

32     }

33 };

vsは親クラス,子クラスの虚表アドレスをコードセグメントに存在させる
コンストラクション関数とコンストラクション関数が呼び出されるたびに、それぞれのダミーテーブルアドレスが戻されます.
親プロファイル:
1 00411B36  mov         dword ptr [eax],offset Base::`vftable' (417814h) 

2         int a = 0;

3 00411B3C  mov         dword ptr [a],0 

親構造:
00411976  mov         dword ptr [eax],offset Base::`vftable' (417814h) 

    {

        clear();

0041197C  mov         ecx,dword ptr [this] 

0041197F  call        Base::clear (411127h) 

    }

サブクラス構造:
00411916  call        Base::Base (411145h) 

0041191B  mov         eax,dword ptr [this] 

0041191E  mov         dword ptr [eax],offset child::`vftable' (417808h) 

00411924  mov         eax,dword ptr [this] 

次の結論が得られます.
newのサブクラスの場合、コンパイラは親クラスと子クラスの虚表エントリアドレスをコードセグメントに保存し、親クラスと子クラスにはそれぞれ虚表があり、構築前に値を割り当てています(子クラスの前にいくつかの値がまだ不明です).
thisポインタはこの新しいクラスのアドレスを保存し,最初の4バイトは虚表のエントリアドレスを格納し,まず親クラスを構築し,親クラスの虚表エントリアドレスに値を付与する.上記の手順の例に従って、親サイズの領域を0にします.サブクラスを構築し,サブクラスのダミーテーブルエントリアドレスを書き込む.
解析の場合、底から上へ解析すると、コンパイラはまず本クラスの虚表エントリアドレスに値を割り当てます.これは、本クラスを正しく解析するためです.再割り当てしないと、サブクラスの関数を呼び出す可能性があるので、サブクラスはすでに解析が完了しています.
コンストラクションとコンストラクション関数では、コンストラクションとコンストラクションのたびにコンパイラがこのクラスの虚関数のエントリ値を再設定し、虚関数を呼び出すのは意味がないため、虚関数は使用できません.