4

私は CPP を初めて使用し、レイト バインディング ポリモーフィズムについて学んでいます。

私が読んで理解したことによると、仮想キーワードは遅延バインディングに使用されます。コンパイル時に内部的に vptr が指す vtable を作成します。たとえば、

class BASE{
public:
virtual void f1(){cout<<"BASE F1\n";}
virtual void f2(){cout<<"BASE F2\n";}
void f3(){cout <<"BASE F3\n";}
};

class D1:public BASE{
public: 
    virtual void f1(){cout<<"D1 F1\n";}
    void f2(){cout<<"D1 F2\n";}  
};

class DD1:public D1{
public:
    void f1(){cout<<"DD1 F1\n";}
    void f2(){cout <<"DD1 F2\n";}
};

ここで、BASE は基本クラスの vtable に 2 つの関数を持ちます。

BASE::f1() 
BASE::f1()

BASE から継承する D1 には、vtable が継承されます。

D1::f1()
BASE::f1

D1 から継承する DD1 には、独自の vtable はありません。

オブジェクトを作成するとき:

//case 1:
BASE *b = new D1(); 
b->f1();//will print "D1 F1"
b->BASE::f1();//will print "BASE F1"
b->f2();//will print "D1 F2"
//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"
b1->D1::f1();//will print "D1 F1"
b1->BASE::f1();//will print"BASE F1"

ただし、次の場合: b1->D1::f1(); そのコンパイルエラーを与える

 error: ‘D1’ is not a base of ‘BASE’

質問: なぜですか? D1 F1 を仮想関数として出力しないでください。

fdumpをスローした後、もう 1 つ興味深いことがわかりました。

Vtable for BASE
BASE::_ZTV4BASE: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4BASE)
16    (int (*)(...))BASE::f1
24    (int (*)(...))BASE::f2

Class BASE
   size=8 align=8
   base size=8 base align=8
BASE (0x7fbc3d2ff120) 0 nearly-empty
    vptr=((& BASE::_ZTV4BASE) + 16u)

Vtable for D1
D1::_ZTV2D1: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI2D1)
16    (int (*)(...))D1::f1
24    (int (*)(...))D1::f2

Class D1
   size=8 align=8
   base size=8 base align=8
D1 (0x7fbc3d31f2d8) 0 nearly-empty
    vptr=((& D1::_ZTV2D1) + 16u)
  BASE (0x7fbc3d2ff180) 0 nearly-empty
      primary-for D1 (0x7fbc3d31f2d8)

Vtable for DD1
DD1::_ZTV3DD1: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI3DD1)
16    (int (*)(...))DD1::f1
24    (int (*)(...))DD1::f2

Class DD1
   size=8 align=8
   base size=8 base align=8
DD1 (0x7fbc3d31f3a8) 0 nearly-empty
    vptr=((& DD1::_ZTV3DD1) + 16u)
  D1 (0x7fbc3d31f410) 0 nearly-empty
      primary-for DD1 (0x7fbc3d31f3a8)
    BASE (0x7fbc3d2ff1e0) 0 nearly-empty
        primary-for D1 (0x7fbc3d31f410)

質問: クラス BASE の仮想テーブルはクラス D1 に継承され、クラス D1 の vTable とクラス BASE は DD1 に継承されませんか? 仮想テーブルの継承はどのように行われますか?

4

5 に答える 5

4

D1 から継承する DD1 には、独自の vtable はありません。

何も間違っていません。仮想関数をオーバーライドするため、独自の vtable を持ちます (基本クラスでvirtual関数が宣言されると、どこでも仮想になるため、ここでキーワードが暗示されます)。virtual

質問: なぜですか? D1 F1 を仮想関数として出力しないでください。

動的タイプがであっても、の静的タイプはです。したがって、その構文はコンパイラに呼び出しを静的に解決するように指示するため、呼び出すことはできません。絶対にこの呼び出しを実行したい場合で、の動的型が実際にある(または派生している) ことがわかっている場合は、それをキャストしてオブジェクトの静的型を変更できます。b1Base*DD1*b1->D1::f1b1b1D1

static_cast<D1*>(b1)->D1::f1();
于 2013-10-05T16:15:04.840 に答える
2

この紛らわしいトピックを明確にするために、優れた本 (無料で入手できる Thinking in C++ をお勧めします) を手に取り、仮想関数の章を読むことをお勧めします。

そうは言っても、あなたが間違っていることはほとんどありません。

引用:BASEから継承するD1は、vtableを継承します:

D1::f1()

BASE::f1

実際には、派生クラスの場合、基本クラスの仮想関数をオーバーライドすることを選択した場合、vtable の内容が置き換えられます。あなたの場合、D1でそれを行いました。したがって、D1 の vtable には D1 関数が含まれます (はい、両方とも f1() と f2() )。

したがって、D1 の VTable は次のとおりです。

D1::f1()
D1::f2()

D1 vTable で基本クラス関数が消える/上書きされる。

DD1 vtable には、D1 の代わりに DD1 の機能が含まれています。

表示されるエラーについては、それに対する回答が既に投稿されています。

于 2013-10-05T16:14:27.290 に答える
2

あなたの期待の少なくともいくつかは間違っています:

// ケース 2:
ベース *b1 = 新しい DD1();
b1->f1();//「DD1 F1」と出力します

おそらく正しい。多分それは「D1 F1」を生成するはずです、言語弁護士、助けてください。

b1->D1::f1();//「D1 F1」を出力します

なぜそれをしなければならないのですか? b1が型である場合、明示的なキャストが必要であると断言したい場合BASE *、コンパイラはオブジェクトが実際に a であると信じる理由がありません。D1

b1->BASE::f1();//「BASE F1」と出力します

はい。

于 2013-10-05T16:16:20.790 に答える
1

「D1 F1 を仮想関数として出力しないのはなぜですか?」

ポインタb1の型がBASE. コンパイラが許可しない BASE オブジェクトから D1 クラスに効果的にアクセスしようとしています。b1次のように、ポインタが有効な D1 オブジェクトであることをコンパイラに通知する必要があります。

dynamic_cast<D1*> (b1) -> f1()
于 2013-10-05T16:09:43.310 に答える
0

クラス D1 についての手がかりがない Base クラスから完全修飾名 (FQN) メソッド呼び出しを行うことはできません。

解決策は次のとおりです-DD1またはD1にダウンキャストしてから、FQN呼び出しを行います:

(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"
于 2013-10-05T16:21:42.227 に答える