2011-03-07 16 views
2

"恐ろしい"ダイヤモンドの継承問題に苦しんでいるはずのケースを見つけたようです。しかし、コードは正常に動作するように見えます。 の場合、私が確かに把握できないことが問題です。サードパーティライブラリを使用したダイヤモンド継承の問題

ここに設定があります。私はMFCを使用しており、マウスクリックウィンドウメッセージのカスタム処理を追加するためにCEditを拡張しています。私はこのクラスとサードパーティ開発者が書いたクラス(この例ではBobと呼んでいます)から継承します。これで、私は特別なコントロールか、ボブのコントロールの拡張バージョンを返すことができるようになりました。問題は、Bobのライブラリを変更することができず、両方のコードが最終的にCEdit(およびその問題に関してCWnd)から継承されることです。

例コード:

class A : public CEdit {...}  // From Bob's library 
class B : public A {...}   // From Bob's library 
class BobsEdit : public B {...} // From Bob's library 

// My version which handles WM_LBUTTONDOWN, WM_CREATE 
// and does a couple other cool things. 
class MyEdit : public CEdit 
{ 
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct) 
    { 
     if (!CEdit::Create(...)) return -1; 

     ...set some style stuff... 
    } 

    afx_msg void OnLButtonDown(UINT nFlags,CPoint point) {} // Override CWnd handler 
} 

class MyBobsEdit : public BobsEdit, public MyEdit {} // My version of Bob's control 


// CBobsUser returns just a standard CEdit and BobsEdit control 
// This is also from Bob's library. 

class CBobsUser 
{ 
    CWnd* GetMeAnEditBox() 
    { 
     CEdit* pEdit; 
     if (...some condition...) 
      pEdit = new CEdit(); 
     else 
      pEdit = new BobsEdit(); 

     ... 
     return pEdit; 
    } 
} 

// CMyUser overrides Bob's GetMeAnEditBox and returns 
// one of my custom controls (with the new cool handler). 
class CMyUser : public CBobsUser 
{  
... 

    CWnd* GetMeAnEditBox() 
    { 
     MyEdit* pEdit; 
     if (...some condition...) 
      pEdit = new MyEdit(); 
     else 
      pEdit = new MyBobsEdit(); 

     ... 
     return pEdit; 
    }     
} 

ので...質問は次のとおりです。

  1. なぜこれがダイヤモンドの継承の問題に苦しむしていないようですか?
  2. このデザインで私が将来私を噛んでしまうかもしれない問題はありますか?
  3. が、私はダイヤモンドの片側にコードを変更できない場合は、この問題を解決するための別の方法である(すなわち、私は両方の脇上の仮想CEditのを宣言することはできません?)

感謝を!

答えて

3

広告1:何も今までにオブジェクトがCBobsEditであることを知っているんだから。オブジェクトをMyBobsEditとして作成しますが、ただちにMyEditにキャストします。すべてのメソッド呼び出しはMyEditにあり、呼び出しエラーは発生せず、キャスト自体もあいまいではありません。 CBobsEditの機能は使用されていません(サブクラスにはメソッドはありません)。それは構築されますが、それは決して親に追加されないので、決して表示されず、決して使用されません。

広告2:まあ、あなたはBobsEditをまったく使用していません。あなたが望むものではないと思います。

広告3:あなたはMyEditそれはテンプレート引数だから継承されたテンプレートを作成し、CEditから1ケースおよび他のケースでCBobsEditから直接それを継承することができます。このテクニックは、しばしば「ミックスイン」と呼ばれます。同様に:

template <typename BaseEditT> 
class MyEdit : public BaseEditT { ... } 

残念ながらMyEdit<CEdit>MyEdit<CBobsEdit>は無関係なクラスです。ポインタをCEdit(これは常に基本クラス)として保存することができれば、インタフェースを定義し、このインタフェースをMyEditに実装し、そのインタフェースへのポインタを格納する必要があります。インターフェイスには、キャスト演算子をCEdit&(およびCEdit const&)にする必要があり、その上でCEditメソッドを呼び出すことができるはずです。このように:

class IMyEdit { 
    virtual operator CEdit &() = 0; 
    virtual operator CEdit const &() const = 0; 
}; 

template <typename BaseEditT> 
class MyEdit : public BaseEditT { 
    operator CEdit &() { return *this; } 
    operator CEdit const &() const { return *this; } 
}; 

。なお、あなたは別のファイルに入れて、あなたが避けるためにCMyUserコンストラクタを定義する場合にのみ、それを含めることができますので、MyEditテンプレートの定義を参照する必要がありますオブジェクトを構築するだけのコードよりコンパイル時のペナルティ。

+0

派生クラスのいずれも仮想関数またはデータメンバーを実装していない場合、それらの間にwilly-nillyをキャストできます。通常、これはC++コミュニティには不明なコンパイラ特有の動作に依存していますが、MFCはこれに大きく依存していますので、安全でなければなりません。 –