2

我似乎找到了一个我应该遭受“可怕的”钻石继承问题的案例。但是,代码似乎工作得很好。我似乎无法确定的是是否存在问题。

这是设置。我正在使用 MFC 并扩展了 CEdit 以添加对鼠标单击窗口消息的自定义处理。然后我继承了这个类和一个第三方开发者编写的类(在这个例子中叫他 Bob)。这样做,我现在可以返回我的特殊控件或 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 虚拟?),是否有另一种方法来解决这个问题

谢谢!

4

1 回答 1

3

广告 1:因为没有人知道对象是CBobsEdit. 您将对象创建为MyBobsEdit,但立即将其强制转换为MyEdit,因此所有方法调用都处于打开状态MyEdit,并且不会出现模棱两可的调用错误,并且转换本身也不模棱两可。从未使用过的功能CBobsEdit(您在子类中没有任何方法)。它是构造的,但它从未添加到父级,因此它从未显示过,也从未使用过。

广告2:嗯,你根本没有使用BobsEdit。我想这不是你想要的。

广告 3:您可以创建MyEdit一个从其模板参数继承的模板,并CEdit在一种情况下直接从另一种情况下继承它CBobsEdit。这种技术通常被称为“mixin”。喜欢:

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 回答