使用第三方库的菱形继承问题
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<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
构造函数的位置包含它,以避免对编译时。
【讨论】:
如果没有派生类实现任何虚函数或数据成员,您可以在它们之间随意转换。通常这会被 C++ 社区所反对,因为它依赖于未记录的特定于编译器的工作,但 MFC 严重依赖于此,所以你应该是安全的。以上是关于使用第三方库的菱形继承问题的主要内容,如果未能解决你的问题,请参考以下文章