wxWidgets和MFC动态类型信息比较

本文详细对比了wxWidgets和MFC中动态类型信息的实现原理。重点介绍了wxWidgets如何利用wxClassInfo类来存储类型信息,并通过宏实现类的注册。同时分析了两者在类型信息存储上的异同。

 

快乐虾

http://blog.youkuaiyun.com/lights_joy/

lights@hb165.com

   

 

本文适用于

wxWidgets- 2.8.8

vs2005

 

欢迎转载,但请保留作者信息

 

wxWidgets被设计为支持动态类型信息。这个支持由wxObject实现,看看:

class WXDLLIMPEXP_BASE wxObject

{

    DECLARE_ABSTRACT_CLASS(wxObject)

……………….

public:

    bool IsKindOf(wxClassInfo *info) const;

………………

};

在这里DECLARE_ABSTRACT_CLASS定义为:

#define DECLARE_ABSTRACT_CLASS(name)                                          /

    public:                                                                   /

        static wxClassInfo ms_classInfo;                                      /

        virtual wxClassInfo *GetClassInfo() const;

让人不由自主想起了CObjectCRuntimeClass

class AFX_NOVTABLE CObject

{

public:

     virtual CRuntimeClass* GetRuntimeClass() const;

…………………..

public:

     BOOL IsKindOf(const CRuntimeClass* pClass) const;

………………………………….

public:

     static const CRuntimeClass classCObject;

……………………………

};

可以认为它们都是同样的想法,都是用一个类来保存类型信息,只不过MFC使用的类(实际声明为struct)叫作CRuntimeClasswxWidgets把它叫做wxClassInfo而已。

1.1    储存的类型信息

同样地比较CRuntimeClasswxClassInfo,可以发现它们储存的类型信息也是几乎一致的:

class WXDLLIMPEXP_BASE wxClassInfo

{

public:

    wxClassInfo( const wxChar *className,

                 const wxClassInfo *baseInfo1,

                 const wxClassInfo *baseInfo2,

                 int size,

                 wxObjectConstructorFn ctor )

        : m_className(className)

        , m_objectSize(size)

        , m_objectConstructor(ctor)

        , m_baseInfo1(baseInfo1)

        , m_baseInfo2(baseInfo2)

        , m_next(sm_first)

        {

            sm_first = this;

            Register();

        }

………………………….

public:

    const wxChar            *m_className;

    int                      m_objectSize;

    wxObjectConstructorFn    m_objectConstructor;

    const wxClassInfo       *m_baseInfo1;

    const wxClassInfo       *m_baseInfo2;

    static wxClassInfo      *sm_first;

    wxClassInfo             *m_next;

    static wxHashTable      *sm_classTable;

……………………..

};

struct CRuntimeClass

{

// Attributes

     LPCSTR m_lpszClassName;

     int m_nObjectSize;

     UINT m_wSchema; // schema number of the loaded class

     CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class

     CRuntimeClass* m_pBaseClass;

…………………………

 

     // CRuntimeClass objects linked together in simple list

     CRuntimeClass* m_pNextClass;       // linked list of registered classes

     const AFX_CLASSINIT* m_pClassInit;

};

从上面列出的两个类可以发现几个不同:

1.              wxClassInfo通过类的构造函数来给wxObject::ms_classInfo这个静态变量赋值,而CRuntimeClass则通过结构体的赋值来构建CObject::classCObject

2.              wxWidgets中,每个类用来储存类型信息的静态变量名称都是一致的,都叫ms_classInfo,而MFC中,每个类储存类型信息的静态变量名称则是不一致的,如CObject中储存类型信息的变量名称为classCObject,而CcmdTarget中储存类型信息的变量名称则为classCCmdTarget

3.              CRuntimeClass中多了一个叫m_wSchema的整数变量,因为MFC被设计为支持序列化。

4.              wxClassInfo中用于指向基类的指针有两个m_baseInfo1m_baseInfo2,而CRuntimeClass则只有一个m_pBaseClass,莫非是wxWidgetsm_baseInfo2来支持多重继承?暂时不是很了解。

5.              CRuntimeClassm_pClassInitm_pfnCreateObject来支持动态创建,wxClassInfo则只使用了m_objectConstructor这个回调函数。

6.              wxClassInfo中多了一个指向类型信息链表头的指针sm_first

7.              wxClassInfo中多了个sm_classTable,从名字猜测似乎是wxWidgets使用哈希表来进行类的快速查找。

1.2    声明支持动态类型

MFC中,如果一个类想要支持动态类型信息,它必须在类的定义中加上DECLARE_DYNAMIC,相应地在类的实现文件中使用IMPLEMENT_DYNAMIC

DECLARE_DYNAMIC的定义为:

#define DECLARE_DYNAMIC(class_name) /

public: /

     static const CRuntimeClass class##class_name; /

     virtual CRuntimeClass* GetRuntimeClass() const; /

IMPLEMENT_DYNAMIC则定义为:

#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) /

     AFX_COMDAT const CRuntimeClass class_name::class##class_name = { /

         #class_name, sizeof(class class_name), wSchema, pfnNew, /

              RUNTIME_CLASS(base_class_name), NULL, class_init }; /

     CRuntimeClass* class_name::GetRuntimeClass() const /

         { return RUNTIME_CLASS(class_name); } /

 

#define IMPLEMENT_DYNAMIC(class_name, base_class_name) /

     IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

就这样,子类保存了父类中表示类型信息的CRuntimeClass指针。

wxWidgets中,在类的声明中使用DECLARE_ABSTRACT_CLASS或者其它在此基础上扩展出来的宏来声明支持动态类型信息。

#define DECLARE_ABSTRACT_CLASS(name)                                          /

    public:                                                                   /

        static wxClassInfo ms_classInfo;                                      /

        virtual wxClassInfo *GetClassInfo() const;

然后使用wxIMPLEMENT_CLASS_COMMON或者在其基础上扩展的宏来给ms_classInfo赋值。

#define wxIMPLEMENT_CLASS_COMMON(name, basename, baseclsinfo2, func)          /

    wxClassInfo name::ms_classInfo(wxT(#name),                                /

            &basename::ms_classInfo,                                          /

            baseclsinfo2,                                                     /

            (int) sizeof(name),                                               /

            (wxObjectConstructorFn) func);                                    /

                                                                              /

    wxClassInfo *name::GetClassInfo() const                                   /

        { return &name::ms_classInfo; }

由于在wxWidgets中是使用了wxClassInfo的构造函数来完成一些变量的赋值,而在此构造函数中调用了Register函数,下面再看看wxClassInfo::Register

// This function wasn't written to be reentrant but there is a possiblity of

// reentrance if something it does causes a shared lib to load and register

// classes. On Solaris this happens when the wxHashTable is newed, so the first

// part of the function has been modified to handle it, and a wxASSERT checks

// against reentrance in the remainder of the function.

 

void wxClassInfo::Register()

{

    if ( !sm_classTable )

    {

        wxHashTable *classTable = new wxHashTable(wxKEY_STRING);

 

        // check for reentrance

        if ( sm_classTable )

            delete classTable;

        else

            sm_classTable = classTable;

    }

 

    // Using IMPLEMENT_DYNAMIC_CLASS() macro twice (which may happen if you

    // link any object module twice mistakenly, or link twice against wx shared

    // library) will break this function because it will enter an infinite loop

    // and eventually die with "out of memory" - as this is quite hard to

    // detect if you're unaware of this, try to do some checks here.

    wxASSERT_MSG( sm_classTable->Get(m_className) == NULL,

        wxString::Format

        (

            _T("Class /"%s/" already in RTTI table - have you used IMPLEMENT_DYNAMIC_CLASS() multiple times or linked some object file twice)?"),

            m_className

        )

    );

 

    sm_classTable->Put(m_className, (wxObject *)this);

 

}

从这个函数可以看出,wxWidgets使用了一个哈希表来完成类名称和wxClassInfo指针之间的映射关系,这点是和MFC不同的,MFC仅仅是将需要动态创建的类的类型信息用单链表链接在一起,而wxWidgets则是将所有的类信息都放在了哈希表中。

至于其它的一些操作如判断子类和父类的关系等等则几乎是一样的,在此不做分析。

   

 

 

 

参考资料

打造windows下的嵌入式开发工具(6)wxWidgets-2.8.8( 2008-9-4 )

dll方式编译wxWidgets-2.8.8( 2008-9-6 )

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值