我最近开始将我的 RAD Studio 2007 项目升级到 RAD Studio 2009。我注意到的一件事是,看似简单的代码突然无法编译。

示例代码:

class CButtonPopupMenu
{
    // Snip

public:
    void Init( TButton* SrcButton )
    {
        SrcButton->OnClick = OnButtonClick;
    }

private:
    void __fastcall OnButtonClick( TObject* Sender )
    {
        // Do some button click stuff
    }
};

// Snip

TButton button = new TButton( this );
TBitBtn bitBtn = new TBitBtn( this );
CButtonPopupMenu popupButton = new CButtonPopupMenu( button );
CButtonPopupMenu popupBitBtn = new CButtonPopupMenu( bitBtn );

这一切都用于编译,但在 2009 年它失败了。查看用于从 TBitBtn 派生的 2007 TButton 的继承链。因此,任何按钮控件(即 OnClick)上预期的事件由 TButton 类共享。因此,我能够将我的 TBitBtn 类视为 TButton

2007 继承链:
  • TBitBtn : TButton

  • 2009 继承链:
  • TBitBtn : TCustomButton
  • TButton : TCustomButton

  • 在 2009 年, TButton TBitButton 都源自 TCustomButton ,我想如果按钮之类的属性保留在那里就好了。如果是这种情况,我可以更改代码以处理 TCustomButton 。不幸的是, TCustomButton 没有像 OnClick 这样的东西。因此,我不能再将 TBitBtn 视为 TButton 。这两个类现在都有自己独立的按钮之类的属性(即它们都声明了自己的 OnClick 事件)。我的意思是,至少提供一个接口(interface)或类似的东西,比如 TButton TBitBtn 实现的 IButton

    这些看似无害的变化似乎会造成不必要的破坏。这看起来很奇怪,我想知道是否有人知道为什么 CodeGear(或任何框架作者)会做这种事情?

    更重要的是,鉴于这种支离 splinter 的继承,是否有优雅的解决方案可以将 TBitBtn 视为 TButton

    最佳答案

    TButton 和 TBitBtn 仍然继续共享一个共同的 OnClick 事件,因为它从一开始就一直在 TControl 级别实现,并且一直如此。 TButton 只是将 protected TControl::OnClick 事件提升为已发布,然后 TBitBtn 将继承该事件。

    在 D2009 中,TCustomButton 与其他 TCustom... 类一样,不会将 protected 成员从基类提升为已发布。 TButton 和 TBitBtn 将 protected TControl::OnClick 事件提升为单独发布。但是事件本身仍然存在于 TControl 级别。

    由于它在 TControl 级别受到保护,因此您可以使用访问器类来访问它,即:

    class TCustomButtonAccess
    {
    public:
        __property OnClick;
    };
    
    class CButtonPopupMenu
    {
        // Snip
    
    public:
        void Init( TCustomButton* SrcButton )
        {
            ((TCustomButtonAccess*)SrcButton)->OnClick = OnButtonClick;
        }
    
    private:
        void __fastcall OnButtonClick( TObject* Sender )
        {
            // Do some button click stuff
        }
    };
    

    或者,对于任何通用的 TControl 指针:
    class TControlAccess
    {
    public:
        __property OnClick;
    };
    
    class CControlPopupMenu
    {
        // Snip
    
    public:
        void Init( TControl* SrcControl )
        {
            ((TControlAccess*)SrcControl)->OnClick = OnControlClick;
        }
    
    private:
        void __fastcall OnControlClick( TObject* Sender )
        {
            // Do some click stuff
        }
    };
    

    一个更优雅的解决方案是改用 RTTI,这也将允许您处理其他类型的对象,例如 TSpeedButton,它们具有自己的 OnClick 事件,即:
    #include <TypInfo.hpp>
    
    class TControlAccess
    {
    public:
        __property OnClick;
    };
    
    class CControlPopupMenu
    {
        // Snip
    
    public:
        void Init( TControl* SrcControl )
        {
            TMethod m;
            m.Code = &OnControlClick;
            m.Data = this;
            SetMethodProp(SrcControl, "OnClick", m);
        }
    
    private:
        void __fastcall OnControlClick( TObject* Sender )
        {
            // Do some click stuff
        }
    };
    

    甚至:
    #include <TypInfo.hpp>
    
    class CObjectPopupMenu
    {
        // Snip
    
    public:
        void Init( TObject* SrcObject )
        {
            TMethod m;
            m.Code = &OnObjectClick;
            m.Data = this;
            SetMethodProp(SrcObject, "OnClick", m);
        }
    
    private:
        void __fastcall OnObjectClick( TObject* Sender )
        {
            // Do some click stuff
        }
    };
    

    关于c++ - TBitBtn 和 TButton 继承链发生了什么变化?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/946316/

    10-11 00:41