27

コンパイラは仮想継承をどのように実装しますか?

次のコードでは:

class A {
  public:
    A(int) {}
};

class B : public virtual A {
  public:
    B() : A(1) {}
};

class C : public B {
  public:
    C() : A(3), B() {}
};

コンパイラーは、B::ctor関数の2つのインスタンスを生成しますか?1つは呼び出しなし、もう1つはA(1)呼び出しありですか?したがって、B::constructorが派生クラスのコンストラクターから呼び出されると、最初のインスタンスが使用されます。それ以外の場合は、2番目のインスタンスが使用されます。

4

5 に答える 5

9

実装に依存します。たとえば、GCC(この質問A(1)を参照)は、2つのコンストラクターを発行します。1つはへの呼び出しがあり、もう1つは呼び出しがありません。

B1()
B2() // no A

Bが構築されると、「フル」バージョンが呼び出されます。

B1():
    A(1)
    B() body

Cが構築されると、代わりに基本バージョンが呼び出されます。

C():
    A(3)
    B2()
       B() body
    C() body

実際、仮想継承がない場合でも2つのコンストラクターが発行され、それらは同一になります。

于 2011-09-09T11:33:55.917 に答える
8

コンパイラーはBの別のコンストラクターを作成しませんが、を無視しA(1)ます。は仮想的に継承されるためA、デフォルトのコンストラクターを使用して最初に構築されます。B()また、が呼び出されたときにすでに作成されているため、そのA(1)部分は無視されます。

編集-コンストラクタ初期化リストのA(3)一部を見逃しました。C仮想継承が使用される場合、最も派生したクラスのみが仮想基本クラスを初期化します。したがって、デフォルトのコンストラクタではなく、で構築されますAA(3)残りはまだ残っています-A中間クラス(ここB)による初期化は無視されます。

上記の実装に関する実際の質問に答えようとして、2を編集します。

Visual Studio(少なくとも2010)では、の2つの実装の代わりにフラグが使用されB()ます。からB実質的に継承するため、のコンストラクターをA呼び出す前にA、フラグがチェックされます。フラグが設定されていない場合、への呼び出しA()はスキップされます。次に、から派生するすべてのクラスBで、フラグは初期化後にリセットされますA。同じメカニズムを使用して、一部の一部である場合にC初期化されないようにします(から継承する場合は、 初期化されます)。ADDCDA

于 2011-09-09T11:15:31.713 に答える
4

いくつかの論文を読むことをお勧めします。これらの2つは、特にC ++の父親からのものであるため、非常に興味深いものです。

[1]ビャーネストロヴルプ。C++の多重継承。C / C ++ユーザージャーナル、1999年5月。

[2]J.Templ。多重継承実装への体系的なアプローチ。ACM SIGPLAN通知、第28巻、第4号1993年4月。

大学で多重継承に関するゼミを(学生として)行う際の主な参考資料として使用しました。

于 2011-09-09T11:49:01.973 に答える
3

Itanium C ++ ABIは、 「これをC++コンパイラで実装するにはどうすればよいか」などのすべての質問に役立つリソースです。

特に5.1.4その他の特殊関数とエンティティには、さまざまな目的のためのさまざまな特殊メンバー関数がリストされています。

<ctor-dtor-name> ::= C1   # complete object constructor
             ::= C2   # base object constructor
             ::= C3   # complete object allocating constructor
             ::= D0   # deleting destructor
             ::= D1   # complete object destructor
             ::= D2   # base object destructor

1.1の定義セクションは便利です(ただし、完全ではありません)。

クラスTのベースオブジェクトデストラクタ

Tの非静的データメンバーおよびTの非仮想直接基本クラスのデストラクタを実行する関数。

クラスTの完全なオブジェクトデストラクタ

基本オブジェクトデストラクタに必要なアクションに加えて、Tの仮想基本クラスのデストラクタを実行する関数。

クラスTのデストラクタを削除する

完全なオブジェクトデストラクタに必要なアクションに加えて、Tの適切な割り当て解除関数(つまり、演算子の削除)を呼び出す関数。

これらの定義から、完全なオブジェクトコンストラクターとベースオブジェクトコンストラクターの目的は明らかです。

于 2012-08-05T06:23:30.077 に答える
0

前述のように、コンパイラの実装によって異なります。

ただし、通常、プログラマーが新しいメソッドを追加するたびに、同じIDを持つ別のメソッドがある場合でも、コードに格納されます。他の場所(「オーバーライド」または「オーバーロード」)。

各メソッドのコードは1回だけ保存されるため、クラスが親クラスから同じメソッドを継承して使用する場合、内部的にはコードへのポインターを使用し、コードを複製しません。

親クラスが仮想メソッドを定義し、子クラスがそれをオーバーライドする場合、両方のメソッドが格納されます。各クラスには「仮想メソッドテーブル」と呼ばれるものがあり、各メソッドへのポインタのテーブルがあります。

パフォーマンスについて心配する必要はありません。コンパイラはメソッドのコードを複製しません。

于 2011-09-09T15:38:53.477 に答える