使用第三方库的菱形继承问题

Posted

技术标签:

【中文标题】使用第三方库的菱形继承问题【英文标题】:A diamond inheritance problem using a third party library 【发布时间】:2011-03-07 07:24:36 【问题描述】:

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

这里是设置。我正在使用 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;
                     

所以...问题是:

    为什么这似乎没有受到钻石继承问题的影响? 此设计是否存在我未发现的问题,可能会在未来困扰我? 如果我不能修改菱形一侧的代码(即我不能在 both 两侧声明 CEdit 虚拟?),是否有其他方法可以解决此问题?

谢谢!

【问题讨论】:

【参考方案1】:

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

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

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

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

很遗憾,MyEdit&lt;CEdit&gt;MyEdit&lt;CBobsEdit&gt; 是不相关的类。如果您可以将指针存储为CEdit(它始终是一个基类),则必须定义一个接口,在 MyEdit 中实现此接口并存储指向该接口的指针。该接口将需要包含CEdit&amp;(和CEdit const&amp;)的强制转换运算符,并且您应该能够在其上调用任何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 构造函数的位置包含它,以避免对编译时。

【讨论】:

如果没有派生类实现任何虚函数或数据成员,您可以在它们之间随意转换。通常这会被 C++ 社区所反对,因为它依赖于未记录的特定于编译器的工作,但 MFC 严重依赖于此,所以你应该是安全的。

以上是关于使用第三方库的菱形继承问题的主要内容,如果未能解决你的问题,请参考以下文章

菱形虚拟继承

菱形继承问题

C++菱形继承问题与虚拟继承原理

C++菱形继承问题与虚拟继承原理

C++菱形继承

C++之继承总结(继承的定义与格式,赋值转换,默认成员函数,菱形继承及菱形虚拟继承)