现在的位置: 首页 > 综合 > 正文

MFC-RTTI

2013年08月23日 ⁄ 综合 ⁄ 共 3213字 ⁄ 字号 评论关闭

关于RTTI

       正如侯杰所说,文档视图是MFC进化为应用程序框架的灵魂,不可否认,这是MFC最为精炒的设计,十多年前数据与表现分离的思想就被应用在这个框架之上。而在文档视图之下,支撑着它的是运行时类型信息(RTTI)

       RTTI允许程序在运行时刻获得类乃至普通类型的信息,这是怎么做到的,其实原理很简单,就是事先将这些信息保存为某种数据结构,保存的工作或由编译器帮你做,或由程序自己完成。程序运行的时候通过某种途径取得这些信息,然后根据这些信息来做运行时判断。

       Delphi是一种RTTI非常强的原生语言,通过TypeInfo函数取得类型信息的进入点,然后你就可以判断它是整型还是字符串,是类还是其他的什么东西。如果是类,你可以取得类名,Published段的成员和方法地址,甚至你可以动态设置属性的值。看看它的快速开发环境,以及组件机制,都离不开RTTI的支持。JavaC#就更强了,C#的反射机制允许查询一个类的所有字段,方法属性以及事件。RTTI反映了一种语言的动态性。

       那么是否RTTI越强就越好呢,不同的应用领域结论会有所不同,RTTI越强表明你要额外存储的信息就越多,对于上层应用来说当然无关紧要,但对于一些底层的开发,空间效率都是必须仔细考虑的,RTTI反而成了一种负担。

       C++几乎是一种全能的语言,它要面对的是各种各样的应用,因而在RTTI这个问题上就不得不慎重行事,因此C++的类型信息仅止于类型的判断,当然你可以扩展,但Bjarne建议尽量少用RTTI。事实上C++背负了太多历史包袱,对很多新特性的支持都是举步维艰的,比如垃圾回收,Bjarne说过:“垃圾回收将使C++不适合做许多底层的工作,而这却正是它的一个设计目标如果原来就把垃圾回收作为C++的一个有机组件部分,那么C++早就可能成为死胎了。

       回到MFC,它并没有使用C++自带的类型信息,可能是那些类型信息根本不够用,也可能是MFC出来的太早(类型信息在1993年才成为C++的一部分)MFC自己设计了一套RTTI机制,并用几个宏来简化代码的编写。

MFCRTTI

       MFC中保存类型信息的是一个叫CRuntimeClass的结构体,声明如下:

struct CRuntimeClass

{

    //类名

    LPCSTR m_lpszClassName;

    //类实例大小 

    int m_nObjectSize;

    //是否支持序列化的标志

    UINT m_wSchema;   

    //创建类实例的函数指针

    CObject* (PASCAL* m_pfnCreateObject)();

    //指向基类的运行时结构

    CRuntimeClass* m_pBaseClass;

    //创建类实例

    CObject* CreateObject();

    //判断是否从某个类继承而来

    BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

 

    //序列化支持

    void Store(CArchive& ar) const;

    static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

   

    //组成运行时结构链表

    CRuntimeClass* m_pNextClass;      

};

       一个具备类型信息的类都必须生成这个运行时结构,并将它保存为自己的静态成员。这个结构提供了三个方面的支持:

1.         类的基本信息,比如类名,对象尺寸,继承关系等。

你可以轻易判断两个类实例是否属于同一个类,比较m_lpszClassName,或比较类的RuntimeClass成员指针。

你可以通过IsDerivedFrom判断两个类的继承关系,前提是这两个类都生成了相应的RuntimeClass

2.         动态创建支持,将类的一个静态成员函数保存进m_pBaseClass,而该函数必然要进行New的操作,比如:

CObject* CTest::CreateObject()

{

    return new CText;

}

       以后我们用pRuntimeClass->CreateObject()来动态创建对象。

3.         序列化支持,这个以后再说。

 

现在假设有一个CMyMainFrm类需要类型信息,它的声明可能是这样:

class CMyMainFrm : public CFrameWnd

{

public:

    //声明一个针对这个类的运行时结构体

static const CRuntimeClass classCMyMainFrm;

    //多态支持

    virtual CRuntimeClass* GetRuntimeClass() const;

    //动态创建支持

    static CObject* PASCAL CreateObject();

public:

    CMyMainFrm();

};

实现则是这样:

CObject* PASCAL CMyMainFrm::CreateObject()

{

    return new CMyMainFrm;

}

 

const CRuntimeClass CMyMainFrm::classCMyMainFrm = {

    "CMyMainFrm",

    sizeof(class CMyMainFrm),

    0xFFFF,

    CMyMainFrm::CreateObject,

    (CRuntimeClass*)(&CFrameWnd::classCFrameWnd),

    NULL};

 

CRuntimeClass* CMyMainFrm::GetRuntimeClass() const

{

    return (CRuntimeClass*)(&CMyMainFrm::classCMyMainFrm);

}

 

CMyMainFrm::CMyMainFrm()

{

}

编写这些代码之后,CMyMainFrm便具备了类型信息,以后通过GetRuntimeClass取得运行时结构就可以在运行时做一些事情了。

与消息映射一样, 每一个类都要写这一堆代码实在是麻烦,使用宏简化一下吧,于是有了DECLARE_DYNCREATE IMPLEMENT_DYNCREATE宏;有些类可能只需要基本的类型判断,而不需要动态创建,于是有了DECLARE_DYNAMICIMPLEMENT_DYNAMIC宏;有些类还需要序列化的能力,于是有了DECLARE_SERIALIMPLEMENT_SERIAL

其中SERIAL包括DYNCREATEDYNCREATE包括DYNAMIC,总结起来如下表所示:

说明

DECLARE_DYNAMI IMPLEMENT_DYNAMIC

基本类信息判断

DECLARE_DYNCREATE

IMPLEMENT_DYNCREATE

基本类信息判断

动态创建

DECLARE_SERIAL

IMPLEMENT_SERIAL

基本类信息判断

动态创建

序列化支持

DYNCREATE为例,CMyMainFrm的声明和实现变成下面这样子:

class CMyMainFrm : public CFrameWnd

{

    DECLARE_DYNCREATE(CMyMainFrm)

public:

    CMyMainFrm();

};

 

IMPLEMENT_DYNCREATE(CMyMainFrm, CFrameWnd)

 

CMyMainFrm::CMyMainFrm()

{

}

 

类型信息使MFC有了一定的动态性,但这种动态性是受到限制的,要达到快速开发的级别仍然很难。不过有它的辅助已经可以完成很多高级的设计工作,比如文档视图,而这将是后面的主题。

 

抱歉!评论已关闭.