【问题标题】:What happened to TBitBtn and TButton inheritance chain?TBitBtn 和 TButton 继承链怎么了?
【发布时间】:2014-06-12 16:54:41
【问题描述】:

我最近开始将我的 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 年它失败了。查看 2007 年的继承链 TBitBtn 用于派生自 TButton。因此,任何按钮控件(即 OnClick)上预期的事件都由 TButton 类共享。因此,我能够将我的TBitBtn 类视为TButton

2007继承链:

  • TBitBtn : TButton

2009继承链:

  • TBitBtn : TCustomButton
  • TButton : TCustomButton

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

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

更重要的是,考虑到这种分散的继承,是否有优雅的解决方案可以将 TBitBtn 视为 TButton

【问题讨论】:

    标签: c++ c++builder vcl c++builder-2009


    【解决方案1】:

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

    在 D2009 中,TCustomButton 与其他 TCustom... 类一样,不会将受保护的成员从基类提升为已发布。 TButton 和 TBitBtn 将受保护的 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
        }
    };
    

    【讨论】:

    • 很好的信息!最后两个 RTTI 示例有什么区别?
    • 第一个对 TObject 指针进行操作,因此适用于所有 VCL 对象类型,无论其层次结构如何,而第二个仅对 TControl 及其后代进行操作。
    【解决方案2】:

    如果这是 Delphi,我建议使用带有 isas 运算符的 TCustomButton 类:

    if (SrcButton is TButton) then
      (SrcButton as TButton).OnClick := OnButtonClick
    else if (SrcButton is TBitButton)
      (SrcButton as TBitButton).OnClick := OnButtonClick;
    

    C++ 太早了

    顺便说一句,VCL 不是有时包含用于在按钮、菜单等和调用代码之间提供单一界面的操作吗?

    【讨论】:

    • 会对您引用的这些操作感兴趣。将不得不查找那个。
    • 在 C++ 中,我认为可能会检查 NULL != dynamic_cast(SrcButton) 以确定 SrcButton 是否实际上是 TButton。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 1970-01-01
    • 2012-05-27
    • 2018-07-21
    • 2012-07-12
    相关资源
    最近更新 更多