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 virtual を宣言できない場合)、これを修正する別の方法はありますか?

ありがとう!

4

1 に答える 1

3

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

広告 2:ええと、あなたはまったく使っていませんBobsEdit。これは、あなたが望んでいたものではないと思います。

広告 3:MyEditテンプレート引数から継承されるテンプレートを作成し、一方のケースから直接継承しCEdit、他方のケースから直接継承することができCBobsEditます。この手法は、「ミックスイン」と呼ばれることがよくあります。お気に入り:

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

残念ながらMyEdit<CEdit>、 とMyEdit<CBobsEdit>は無関係なクラスです。ポインターを (常に基本クラスとして) 格納することができる場合はCEdit、インターフェイスを定義し、このインターフェイスを MyEdit に実装して、そのインターフェイスへのポインターを格納する必要があります。CEdit&インターフェイスには、 (and )へのキャスト演算子が含まれている必要があり、その上で任意のメソッド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; }
};

オブジェクトを構築するコードだけがテンプレートの定義を確認する必要があることに注意してください。そのMyEditため、別のファイルに入れて、CMyUserコンストラクターを定義する場所にのみ含めることができ、コンパイル時のペナルティを回避できます。

于 2011-03-07T11:02:06.623 に答える