C++虚関数メカニズム学習

25121 ワード

お礼を言う
 
本文はの読書とgdbの使用に基づいて完成した.ここでLippmanのcfront中の対象モデルに対する解析に感謝し、これらの解析は読者の霧を解くのに役立つ.また、Linuxの下でこの上なく強大なgdbツールは更に“暗い”の“明かり”を追い払うのです. :)
 
No-Inheritance
 
 1 class Base {

 2     public:

 3         int a = 21;

 4         static int b;

 5         int c = 22;

 6 

 7         void showBase1();

 8         static int showBase2();

 9 };

10 

11 void Base::showBase1() {

12     cout<<"Base"<<endl;

13 }   

14 int Base::showBase2() {

15     cout<<"base2"<<endl;

16 }

解析:
GDBを使用してメモリ領域を表示し、表示する
Baseにおけるaのアドレスは0 x 7 ffffffdc 50
Base中のcのアドレスは0 x 7 ffffffdc 54
Baseのbのアドレスは0 x 601068です
showBase 1のアドレスは0 x 40085 e(パラメータはBase*const->thisポインタは定数ポインタ)
showBase 2のアドレスは0 x 400888(パラメータはvoid)
明らかに
non-static data memberはclass objectに格納されます.
static data member,member functionは本classのすべてのオブジェクトがshareされているため、共通領域に配置されている.
 
Inheritance without Polymorphism
 1 class Base {

 2     public:

 3         int a = 21;

 4         static int b;

 5         int c = 22;

 6 

 7         void showBase1();

 8 };      

 9 int Base::b = 10;

10 void Base::showBase1() {

11     cout<<"Base"<<endl;

12 }   

13 

14 class Inheri : public Base {

15     public:

16         int c = 23;

17         static int d;

18         

19         static int showInheri1();

20 };      

21 int Inheri::d = 11;

22 int Inheri::showInheri1(){

23     cout<<"Inheri"<<endl;

24 }

解析:
GDBを使用してメモリ領域を表示し、表示する
Inheriにおけるaのアドレスは0 x 7 fffffffdc 70
Inheri中のcのアドレスは0 x 7 fffffdc 78
Inheriのbのアドレスは0 x 601068です
Inheriにおけるbのアドレスは0 x 60106 c
showBase 1のアドレスは0 x 40085 e(パラメータはBase*const->thisポインタは定数ポインタ)
showInheri 1のアドレスは0 x 400888(パラメータはvoid)
明らかに
derived class objectには、ベースクラスと派生クラスのnon-static data memberが含まれています.
 
No-Inheritance with Polymorphism
 
 1 class Base {

 2     public:

 3         int a = 21;

 4         static int b;

 5         int c = 22;

 6 

 7         virtual void showBase1();//virtual function

 8 };      

 9 int Base::b = 10;

10 void Base::showBase1() {

11     cout<<"Base"<<endl;

12 }

gdbを使用してbase class object(p ptr)を表示する
     " {_vptr.Base = 0x400af0 , a = 21, static b = 10, c = 22} "
クラスでのダミーメカニズムの使用(ダミー関数、ダミーベースクラス、ダミー継承...この場合、objectごとにvptrが追加する、対応するvtblを指す.
  
gdbを使用してvptrが指す虚関数(p/a*(void**)0 x 400 af 0を表示
    " {0x40092e , 0x697265686e4936} " 
  
Single-Inheritance with Virtual Mechanism
 
class Base {

    public:

        int a = 21;

        static int b;

        int c = 22;



        virtual void showBase1();

};

int Base::b = 10;

void Base::showBase1() {

    cout<<"Base"<<endl;

}



class Inheri : public Base {

    public:

        int c = 23;

        static int d;



        static int showInheri1();

};

int Inheri::d = 11;

int Inheri::showInheri1(){

    cout<<"Inheri"<<endl;

}

Inheri class objectの表示
    { = {_vptr.Base = 0x400bc0 , a = 21, static b = 10, c = 22}, c = 23, static d = 11}
Ineri class objectのvtblの表示
    0x400998
derived class objectはbase class subobjectから継承するvptrを直接使用していることがわかる.
 
同様に、Base class objectを表示
    {_vptr.Base = 0x400be0 , a = 21, static b = 10, c = 22}
Base class objectのvtblの表示
    0x40096e          
  
ここから、Single Inheritanceにおける各class objectのvtblには、本classに対応するvirtual functionのみが含むことがわかる.
  
derived classがbase class pointerに付与された場合をもう一度テストします.
    Base* bbptr = new Inheri;
bbptrが指すメモリを表示するには、次の手順に従います.
    { = {_vptr.Base = 0x400bc0 , a = 21, static b = 10, c = 22}, c = 23, static d = 11}
vtblの内容を表示するには、次の手順に従います.
    0x400998
ここでは2つの発見があります.
1)ベースクラスポインタBase*を使用してInheriオブジェクトを接続するが、vptrが指すvtblは依然としてInheri classである.
2)各classに対応するvtblはメモリに1部しかなく、bbprとiptrが指すvtblはテスト中に0 x 400 bc 0にある
 
Multiple-Inheritance with Virtual Mechanism
    
 1 class Base {

 2     public:

 3         int a = 21;

 4         static int b;

 5         int c = 22;

 6 

 7         virtual void show();//inline

 8 };

 9 int Base::b = 10;

10 void Base::show() {

11     cout<<"Base"<<endl;

12 }

13 

14 class Inheri : public Base {

15     public:

16         int c = 23;

17         static int d;

18 

19          virtual void show();

20 };

21 int Inheri::d = 11;

22 void Inheri::show(){

23     cout<<"Inheri"<<endl;

24 }

25 

26 class OtherBase{

27     public:

28         int oa;

29         virtual void show();

30 };

31 void OtherBase::show(){

32     cout<<"OtherBase"<<endl;

33 }

34 

35 class Final : public OtherBase,public Inheri{

36     public:

37         virtual void show();

38 };                                                                                                                                                                                             

39 void Final::show() {

40     cout<<"Final"<<endl;

41 }

Finalオブジェクトの表示
    "{ = {_vptr.OtherBase = 0x400cd0 , oa = 0}, = { = {_vptr.Base = 0x400ce8 , a = 21, static b = 10, c = 22}, c = 23, static d = 11}, }"
次のように表示されます.
1)サブオブジェクトの右から左へのコンストラクション
2)それぞれOtherBaseとBaseのvptrを含む、これはderived class objectがbase class objectに付与されたときに扱いやすいためである.
OtherBaseとBaseのvptrの情報の表示を続行
    _vptr.OtherBaseが指すvtblの情報は、0 x 400 a 5 c
      _vptr.Baseが指すvtblの情報は:0 x 400 a 86
この2つのvtblが保存されているのはすべてFinal::show.したがって、Finalによって各ベースクラスのポインタに値を付与場合、最後には常にFinal自身の虚関数が呼び出される. 
 
Virtual Inheritance
  
 1 class _ios {

 2     public:

 3         int i;

 4         virtual void show();

 5 };

 6 void _ios::show() {

 7     cout<<"ios"<<endl;

 8 }

 9 

10 

11 class _istream : public _ios {

12     public:

13         int is; 

14         virtual void show();

15 };

16 void _istream::show() {

17     cout<<"istream"<<endl;

18 }

19 

20 class _ostream : public _ios {

21     public:

22         int os; 

23         virtual void show();

24 };

25 void _ostream::show() {

26     cout<<"ostream"<<endl;

27 }

28 

29 class _iostream : public _istream, public _ostream {

30     public:

31         int ios;

32         virtual void show();

33 };

34 void _iostream::show() {

35     cout<<"iostream"<<endl;

36 }

virtual inheritanceを使用していない場合は、_を表示します.iostreamオブジェクト、2つのファイルが表示されます.iosクラスのオブジェクトは、それぞれ_に属します.istreamと_ostream : 
    "{<_istream> = {<_ios> = {_vptr._ios = 0x400c90 , i = 0}, is = 0}, <_ostream> = {<_ios> = {
_vptr._ios = 0x400ca8 , i = 0}, os = 0}, ios = 0}"
前に紹介したsingle inheritance with polymorphism,istreamと_ostreamはそれぞれ従_を使用しますiosから来たvptr.iosは自分のvtblを指します. 
virtual inheritanceを使用すると、1部しか見えません.iosオブジェクト:
    "{<_istream> = {<_ios> = {_vptr._ios = 0x400d18 , i = 0}, _vptr._istream = 0x400cd8 , is = 0}, <_ostream> = {_vptr._ostream = 0x400cf8 , os = 0}, ios = 0}"
虚継承では,単一継承と同様にベースクラスを継承するvptrはなく,独自のvptrを持つ.
vtblの内容を引き続き表示し、それぞれを表示します.
    0x400a40 <_ZTv0_n24_N9_iostream4showEv>
    0x400a3a <_ZThn16_N9_iostream4showEv>
虚関数テーブルの関数もすべて_iostream classのmember function.したがって、iostreamオブジェクトがそのbase class subobjectに付与するポインタは、常に_に呼び出されます.iostream classのvirtual function.
 
Reference
  
  
 
コメント
詳細については、https://github.com/CarlSama/Inside-The-CPP-Object-Model-Reading-Notesを参照してください.