使用第三方库的钻石继承问题
我似乎发现了一个案例,我应该遭受“可怕的”钻石继承问题的困扰。然而,该代码似乎工作得很好。我似乎无法确定是否存在问题。
这是设置。我正在使用 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;
}
}
那么...问题是:
- 为什么这似乎没有遇到钻石继承问题?
- 这个设计是否有一个我没有看到的问题可能会在未来困扰我?
- 如果我无法修改菱形一侧的代码(即我无法在两侧声明 CEdit 虚拟?),是否有另一种方法可以解决此问题?)
谢谢!
I seem to have found a case where I should be suffering from the "dreaded" diamond inheritance problem. However, the code appears to work just fine. What I can't seem to figure out for sure is if there could be a problem.
Here is the setup. I am using MFC and have extended CEdit to add custom handling of a mouse click windows message. I then inherit from this class and a class written by a third party developer (call him Bob for this example). Doing this, I can now return either my special control or an enhanced version of Bob's control. Problem is, Bob's library can not be modified and both our code ultimately inherits from CEdit (and CWnd for that matter).
Example code:
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;
}
}
So... Questions are:
- Why does this not seem to suffer from the diamond inheritance problem?
- Is there an issue I don't see with this design that could bite me in the future?
- Is there another way to fix this if I can't modify code on one side of the diamond (i.e. I can't declare CEdit virtual on both sides? )
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
广告 1:因为没有人知道该对象是
CBobsEdit
。您将对象创建为MyBobsEdit
,但立即将其强制转换为MyEdit
,因此所有方法调用都在MyEdit
上,并且不会出现不明确的调用错误,并且演员本身也不含糊。从未使用过 CBobsEdit 的任何功能(子类中没有任何方法)。它已被构造,但从未添加到父级,因此从未显示且从未使用。广告 2: 嗯,您根本没有使用
BobsEdit
。我想这不是你想要的。广告 3:您可以将
MyEdit
设为从其模板参数继承的模板,并在一种情况下直接从CEdit
继承,并从CBobsEdit。这种技术通常被称为“mixin”。例如:
不幸的是
MyEdit
和MyEdit
是不相关的类。如果您可以将指针存储为 CEdit(始终是基类),则必须定义一个接口,在 MyEdit 中实现此接口并将指针存储到该接口。该接口需要包含一个到CEdit&
(和CEdit const&
)的转换运算符,并且您应该能够调用任何CEdit
其上的方法。像这样:注意,只有构建对象的代码才需要查看
MyEdit
模板的定义,因此您可以将其放在单独的文件中,并仅在定义CMyUser 的位置包含它
构造函数以避免编译时的损失。Ad 1: Because nothing ever knows the object is
CBobsEdit
. You create the object asMyBobsEdit
, but immediately cast it toMyEdit
, so all method calls are onMyEdit
and no abiguous call errors arise and the cast itself is not ambiguous either. No functionality ofCBobsEdit
is ever used (you don't have any methods in the subclass). It is constructed, but it is never added to a parent, so it's never shown and never used.Ad 2: Well, you are not using
BobsEdit
at all. Which, I suppose, is not what you wanted.Ad 3: You can make
MyEdit
a template that is inherited from it's template argument and inherit it directly fromCEdit
in one case and fromCBobsEdit
in the other case. This technique is often called "mixin". Like:Unfortunately
MyEdit<CEdit>
andMyEdit<CBobsEdit>
are unrelated classes. If you can do with storing the pointer asCEdit
(which is always a base class), you'll have to define an interface, implement this interface in MyEdit and store pointer to that interface. The interface will need to contain a cast-operator toCEdit&
(andCEdit const&
) and than you should be able to call anyCEdit
methods on it. Like this:Note, that than only the code constructing the objects will need to see definition of the
MyEdit
template, so you can put it in a separate file and include it only where you defineCMyUser
constructor to avoid the penalty on compile-time.