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のすべての継承属性の仮想属性を作成します。
まあ、Dreaded Diamondの素晴らしいところは、それが発生したときにエラーになることです。回避する最善の方法は、事前に継承構造を理解することです。たとえば、私が取り組んでいるプロジェクトの1つに、閲覧者と編集者がいます。エディターはビューアーの論理サブクラスですが、すべてのビューアーはサブクラス(TextViewer、ImageViewerなど)であるため、エディターはビューアーから派生しないので、最終的なTextEditor、ImageEditorクラスはひし形を回避できます。
ひし形が避けられない場合は、仮想継承を使用します。ただし、仮想ベースの最大の注意点は、仮想ベースのコンストラクタmustが最も派生したクラスによって呼び出されることです。つまり、仮想的に派生したクラスは、コンストラクタパラメータを制御できません。また、仮想ベースの存在により、チェーンを介したキャストでパフォーマンス/スペースのペナルティが発生する傾向がありますが、最初のペナルティ以上のペナルティは多くないと思います。
さらに、使用するベースが明確である場合は、いつでもダイヤモンドを使用できます。時々それが唯一の方法です。
委任による継承を使用します。次に、両方のクラスがベースAをポイントしますが、Aにリダイレクトするメソッドを実装する必要があります。これには、Aの保護されたメンバーをB、C、およびDの「プライベート」メンバーに変換するという副作用がありますが、今は仮想を必要とし、あなたはダイヤモンドを持っていません。
私はより良いクラスデザインを提案します。多重継承によって最もよく解決されるいくつかの問題があると確信していますが、最初に別の方法があるかどうかを確認してください。
そうでない場合は、仮想関数/インターフェースを使用します。