1

次のケースでvirtualは、クラス A のサブオブジェクトを B および C と共有するダイヤモンド問題を解決するために使用されます。

例えば:

class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };

このタイプの継承は、コンパイル時または実行時に解決されますか? つまり、関数とクラスで使用された場合、virtual の意味はどのように異なるのでしょうか? 仮想を使用して基本クラスから継承する場合、動的バインディングの概念はありますか?

4

4 に答える 4

1

仮想継承は実行時に解決されます。

仮想継承は、仮想関数と同様に、クラスの各インスタンスが、仮想のものを見つけることができる場所を記述するランタイム データにアクセスできることを意味します。

How C++ virtual inheritance is implemented in compilers? でよく説明されています。

于 2012-01-01T15:22:08.560 に答える
1

あなたが言うように、継承は「解決」する必要はないので、質問はあまり明確ではありません。

重要な違いは、通常の継承と仮想継承の違いです。通常、つまり を継承する場合B : AC : AクラスD : B, Cにはタイプ の2 つのサブクラスA、つまりD::B::AとがありD::C::Aます。一方、 が実質的に から継承する場合B、最終的な型を定義するまで、最終的なサブクラスの構成は延期されます。つまり、とのいずれかをインスタンス化すると、それ自体が実体となる仮想サブクラスを持っています。一方、クラスからさらに派生する場合、最も派生したクラスには type のサブクラスが1 つだけ含まれます。CAB : virtual AC : virtual AABCA

おそらく、メンバー関数との類推を検討したいかもしれません。各派生クラスが基本関数と同じ名前のメンバー関数を追加すると、いくつかの異なる関数が作成されます。一方、基本関数が の場合、最も派生したクラスで定義されている関数は1 つvirtualしかありません。各中間クラスにはまだある種の関数がありますが (関数が純粋仮想ではない場合)、「アクティブな」定義を定義するのは最終クラスだけです。

仮想継承はコンストラクターに影響を与えます。つまり、仮想ベース コンストラクター (例の場合) は、またはではなく、最も派生したクラス (つまり、A()) によって直接呼び出されます。もしそうなら、これは、サブクラスが1つしか含まれていないことを「知っている」ためであり、直接「責任を負う」からです。究極の最も派生したクラスに道を譲る仮想プレースホルダーを保持するだけです。DBCDABC

基本ポインタ/参照を介してメンバー関数にアクセスすると、期待どおりに動作し、通常の方法で (つまり、一般に動的に) 解決されますが、これは仮想継承とは関係ありません。実際の関数ルックアップは、余分なレベルの間接参照が含まれる可能性があるため、もう少し複雑になる可能性がありますが、基本的なことは何も変わりません。

于 2012-01-01T15:30:19.540 に答える
1

このタイプの継承は、コンパイル時または実行時に解決されますか?

継承は型の形状(メモリ フットプリント) を定義し、常にコンパイル時に解決されます。一方、基本型のメンバーへのアクセスは実行時に解決されます。これは、階層内の中間型が、基本サブオブジェクトが最も派生した型のメモリ内のどこに配置されるかを認識できないためです。

つまり、関数とクラスで使用された場合、virtual の意味はどのように異なるのでしょうか?

タイプと機能はすべてのレベルで異なるため、ここで言うことはあまりありません。唯一の共通点は、コンパイル時に完全に解決できない部分があることです。特に、類似点は、仮想関数を使用するコードが vptr (仮想テーブル ポインター) に依存して、最も派生した型の適切な vtable (仮想テーブル) を見つけることです。同様に、ベース オブジェクトへのアクセスには、基本から仮想的に派生する型の各サブオブジェクトに格納されたポインターの使用。

Itanium C++ ABI、特にNon-POD Class Typesで詳細を読むことができます (特定の実装について、これはすべて標準の一部ではありません) 。

簡単にまとめると、型 D が型 B から事実上継承するときはいつでも、D サブオブジェクトには B サブオブジェクトへの隠しポインターが含まれます。そのポインターの必要性は、D に対する B サブオブジェクトの相対位置が、さらに継承されると変化する可能性があるという事実から生じます。

于 2012-01-01T16:34:08.433 に答える
0

仮想クラスがコンパイル時のあいまいさを解決することは私の理解です。たとえば、B と C の両方に getSize メソッドがあるとします。

virtual がない場合、D.getSize への呼び出しは基本クラスの getSize メソッドを使用しようとします。B と C の両方に getSize メソッドがあるため、コンパイラはあいまいさを適切に解決できず、コードはコンパイルされません。

virtual では、D.getSize への呼び出しは、コードが正しくコンパイルされるように、派生した getSize メソッドを使用します。

于 2012-01-01T15:29:31.173 に答える