使用第三方库的钻石继承问题

时间:2011-03-07 07:24:37

标签: c++ multiple-inheritance diamond-problem

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

这是设置。我正在使用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 个答案:

答案 0 :(得分: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&(和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构造函数定义到的位置包含它避免在编译时受到惩罚。