多重継承を使用する場合、どうすれば死のダイヤモンドを回避できますか?
-
02-07-2019 - |
質問
http://en.wikipedia.org/wiki/Diamond_problem
それが何を意味するかは知っていますが、それを回避するためにどのような手順を踏むことができますか?
解決
実用的な例:
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
クラスDがB&の両方から継承する方法に注意してください。 C.しかし、B& CはAを継承します。その結果、クラスAの2つのコピーがvtableに含まれます。
これを解決するには、仮想継承が必要です。事実上継承する必要があるのはクラスAです。したがって、これにより問題が修正されます。
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
他のヒント
仮想継承。それが目的です。
インターフェイスの多重継承のみを使用します。クラスの多重継承は魅力的な場合もありますが、定期的に依存していると混乱を招き、痛みを伴うこともあります。
継承は強力で強力な武器です。本当に必要な場合にのみ使用してください。過去において、ダイヤモンドの継承は、ユーザーが「従業員」であると言って、私が分類をはるかに超えていた兆候でした。しかし、これらは「ウィジェットリスナー」でもありますが、...
これらの場合、複数の継承の問題に遭遇するのは簡単です。
構成と所有者へのポインタを使用して、それらを解決しました:
前:
class Employee : public WidgetListener, public LectureAttendee
{
public:
Employee(int x, int y)
WidgetListener(x), LectureAttendee(y)
{}
};
後:
class Employee
{
public:
Employee(int x, int y)
: listener(this, x), attendee(this, y)
{}
WidgetListener listener;
LectureAttendee attendee;
};
はい、アクセス権は異なりますが、コードを複製せずにそのようなアプローチで逃げることができれば、それはあまり強力ではないので良いです。 (代替手段がない場合は、電力を節約できます。)
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
これにより、クラスAの属性がクラスDで2回繰り返され、メモリ使用量が増加します。したがって、メモリを節約するために、Vtableに格納されるクラスAのすべての継承属性の仮想属性を作成します。
まあ、恐ろしいダイヤモンドの素晴らしいところは、それが発生したときにエラーになることです。回避する最善の方法は、事前に継承構造を把握することです。たとえば、私が取り組んでいるプロジェクトには、ビューアとエディタがあります。エディターはビューアーの論理サブクラスですが、すべてのビューアーはサブクラス(TextViewer、ImageViewerなど)であるため、エディターはビューアーから派生しないため、最終的なTextEditor、ImageEditorクラスはダイヤモンドを回避できます。
ダイヤモンドが回避できない場合、仮想継承を使用します。ただし、仮想ベースに関する最大の警告は、仮想ベースのコンストラクターが最も派生したクラスによって呼び出される必要があることです。つまり、派生するクラスは、コンストラクターパラメーターを実質的に制御できないことを意味します。また、仮想ベースの存在は、チェーンを介してキャストする際にパフォーマンス/スペースのペナルティを被る傾向がありますが、最初のものよりも多くのペナルティがあるとは思いません。
さらに、使用するベースを明示的に指定すると、いつでもダイヤモンドを使用できます。時々それが唯一の方法です。
より良いクラス設計を提案します。多重継承によって最適に解決される問題があるはずですが、最初に別の方法があるかどうかを確認してください。
そうでない場合は、仮想機能/インターフェースを使用します。
委任による継承を使用します。次に、両方のクラスはベースAを指しますが、Aにリダイレクトするメソッドを実装する必要があります。 B、C、およびDのメンバーですが、仮想は必要なく、ダイヤモンドはありません。