多重継承を使用する場合、どうすれば死のダイヤモンドを回避できますか?

StackOverflow https://stackoverflow.com/questions/137282

  •  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のメンバーですが、仮想は必要なく、ダイヤモンドはありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top