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

发布于 2024-10-20 09:04:57 字数 1811 浏览 8 评论 0原文

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

这是设置。我正在使用 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 虚拟?),是否有另一种方法可以解决此问题?)

谢谢!

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:

  1. Why does this not seem to suffer from the diamond inheritance problem?
  2. Is there an issue I don't see with this design that could bite me in the future?
  3. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如痴如狂 2024-10-27 09:04:57

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

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

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

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

不幸的是 MyEditMyEdit 是不相关的类。如果您可以将指针存储为 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 的位置包含它 构造函数以避免编译时的损失。

Ad 1: Because nothing ever knows the object is CBobsEdit. You create the object as MyBobsEdit, but immediately cast it to MyEdit, so all method calls are on MyEdit and no abiguous call errors arise and the cast itself is not ambiguous either. No functionality of CBobsEdit 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 from CEdit in one case and from CBobsEdit in the other case. This technique is often called "mixin". Like:

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

Unfortunately MyEdit<CEdit> and MyEdit<CBobsEdit> are unrelated classes. If you can do with storing the pointer as CEdit (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 to CEdit& (and CEdit const&) and than you should be able to call any CEdit methods on it. Like this:

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; }
};

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 define CMyUser constructor to avoid the penalty on compile-time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文