あいまいさが D にあるのに、B と C で仮想継承を実行する必要があるのはなぜですか? Dにしたらもっと直感的だったのに。
あなたの例では、B と C はvirtual
、コンパイラに A のコピーが 1 つだけ含まれていることを確認するために具体的に使用しています。彼らがこれをしなかった場合、彼らは事実上、「独自の A 基底クラスが必要です。他の派生オブジェクトと共有するつもりはありません」と言っています。これは非常に重要です。
仮想基底クラスを共有したくない例
A が何らかのコンテナである場合、B はそこから派生し、特定のタイプのオブジェクトを格納します。たとえば、「Bat」を格納し、C は「Cat」を格納します。D が B と C が別々にコウモリとネコの個体群に関する情報を提供することを期待している場合、C の操作がコウモリに対して/一緒に何かをした場合、または B の操作が猫に対して/と何かをした場合、D は非常に驚くでしょう。
仮想基底クラスを共有したい例
D が A にあるいくつかの関数またはデータ メンバーへのアクセスを提供する必要があるとします。たとえば、「A::x」とします... A が B と C によって独立して (非仮想的に) 継承される場合、コンパイラは D を解決できません。 ::x を B::x または C::x に、プログラマが明示的に明確にする必要はありません。これは、派生チェーンによって暗示される「is-a」関係が 1 つではなく 2 つあるにもかかわらず、D を A として使用できないことを意味します (つまり、B "が"A であり、D" が "B" である場合、ユーザーはD が「A」であるかのように D を使用することを期待/必要とする)。
この機能が標準化委員会によってこのように設計されているのはなぜですか?
virtual
継承が存在するのは、それが役立つ場合があるからです。これは、B と C の設計に関して侵入的な概念であり、B と C のカプセル化、メモリ レイアウト、構築と破棄、および関数のディスパッチにも影響するため、D ではなく B と C で指定されています。
B クラスと C クラスがサードパーティ ライブラリから来ている場合、どうすればよいでしょうか?
D が両方から継承し、A へのアクセスを提供する必要があるが、B と C が仮想継承を使用するように設計されておらず、変更できない場合、D は、A API に一致する要求を B と B のいずれかに転送する責任を負う必要があります。 /または C および/またはオプションで別の A から直接継承します (目に見える「is A」関係が必要な場合)。呼び出し元のコードが D を処理していることを認識している場合 (テンプレートを使用していても) は実用的ですが、基本クラスへのポインターを介したオブジェクトの操作では、D が実行しようとしている管理について認識されず、すべてが正しくするのは非常に難しいです。しかし、それは「ベクトルが必要で、リストしかない場合はどうなるか」、「ドライバーではなくノコギリ」と言っているようなものです...まあ、それを最大限に活用するか、本当に必要なものを手に入れてください.
編集: 私の答えは、派生オブジェクトが作成されるたびに A のコンストラクターを呼び出すべきではないことを B および C クラスに示すことでした。これは、D によって呼び出されるためです。
それはこれの重要な側面です、はい。