仮想基本クラス

クラスは派生クラスへの間接基底クラスであることが複数回可能であるため、C++ にはこのような基底クラスの動作を最適化する方法が用意されています。 仮想基底クラスは、領域を節約し、多重継承を使用するクラス階層でのあいまいさを避ける方法を提供します。

非仮想オブジェクトはそれぞれ、基底クラスで定義されたデータ メンバーのコピーを含んでいます。 この重複によって領域が浪費され、基底クラスのメンバーのコピーにアクセスするたびに、どちらのコピーかを指定しなければならなくなります。

仮想基底クラスとして指定された基底クラスは、データ メンバーを複製しなくても、間接基底クラスとして複数回使用できます。 データ メンバーの 1 つのコピーが、仮想基底クラスとして使用するすべての基底クラスで共有されます。

仮想基底クラスを宣言すると、virtual キーワードが派生クラスの基底クラスのリストに表示されます。

昼食の行列をシミュレートした、次の図のクラス階層構造を考えます。

Lunch-Line シミュレーション グラフ

Lunch-Line シミュレーション グラフ

図で、Queue は、CashierQueue および LunchQueue の基底クラスです。 ただし、LunchCashierQueue を作成するために両方のクラスを組み合わせると、新しいクラスに、Queue 型のサブオブジェクトが 2 つ (1 つは CashierQueue のサブオブジェクト、もう 1 つは LunchQueue のサブオブジェクト) が含まれるという問題が生じます。 次の図は、概念的なメモリ レイアウトを示します (実際のメモリ レイアウトは最適化される場合があります)。

Lunch-Line シミュレーション オブジェクト

Lunch-Line シミュレーション オブジェクト

LunchCashierQueue オブジェクトに 2 つの Queue サブオブジェクトがあることに注意してください。 次のコードは、Queue が仮想基底クラスであることを宣言します。

// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};

virtual キーワードにより、サブオブジェクト Queue のコピーは 1 つだけになります (次の図を参照)。

仮想基底クラスを持つ、シミュレートされた Lunch-Line オブジェクト

仮想基底クラスを持つ、シミュレートされた Lunch-Line オブジェクト

クラスは、指定された型の仮想コンポーネントと非仮想コンポーネントの両方を持つことができます。 これは、次の図に示されている条件で発生します。

同一クラスの仮想および非仮想コンポーネント

同一クラスの仮想および非仮想コンポーネント

図では、CashierQueue と LunchQueue は仮想基底クラスとして Queue を使用します。 ただし、TakeoutQueue は、仮想基底クラスではなく、基底クラスとして Queue を指定します。 したがって、LunchTakeoutCashierQueue には型 Queue の 2 つのサブオブジェクトがあります。1 つは LunchCashierQueue を含む継承パスからのもので、もう 1 つは TakeoutQueue を含むパスからのものです。 これを次の図に示します。

仮想および非仮想継承によるオブジェクトのレイアウト

オブジェクトのレイアウトにおける仮想および非仮想継承

注意

仮想継承は、非仮想継承と比較してサイズに関して大きな利点があります。ただし、余分な処理オーバーヘッドが生じる場合があります。

派生クラスが仮想基底クラスから継承する仮想関数をオーバーライドする場合、および派生基底クラスのコンストラクターまたはデストラクターが仮想基底クラスへのポインターを使用してその関数を呼び出す場合、コンパイラは仮想基底クラスを含むクラスに追加の vtordisp 隠しフィールドを導入する場合があります。 /vd0 コンパイラ オプションは、隠された vtordisp コンストラクター/デストラクター ディスプレイスメント メンバーの追加を抑制します。 /vd1 コンパイラ オプション (既定) は、これらを必要に応じて有効にします。 すべてのクラス コンストラクターとデストラクターが仮想的に仮想関数を呼び出すことが確実な場合にだけ、vtordisp をオフにしてください。

/vd コンパイラ オプションは、コンパイル モジュール全体に影響します。 vtordisp プラグマを使用すると、vtordisp フィールドをクラス単位で無効化したり、再度有効化したりできます。

#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )

参照

関連項目

複数の基本クラス