再谈 动态创建对象DECLARE_DYNCREATE(DECLARE_DYNAMIC/DECLARE_DYNAMIC)

来源:互联网 发布:java重载函数是什么 编辑:程序博客网 时间:2024/06/05 00:22

百度一下都是说让派生类支持动态创建,可是给出详细解释的却很少,下面就来介绍一下。

首先看他俩的声明:
// not serializable, but dynamically constructable
#define DECLARE_DYNCREATE(class_name) \
        DECLARE_DYNAMIC(class_name) \
        static CObject* PASCAL CreateObject();

#define _DECLARE_DYNCREATE(class_name) \
        _DECLARE_DYNAMIC(class_name) \
        static CObject* PASCAL CreateObject();

看来 DECLARE_DYNCREATE 内部使用了 DECLARE_DYNAMIC 宏
那么 IMPLEMENT_DYNCREATE 内部也应该使用 IMPLEMENT_DYNAMIC宏
我们来看看是不是这样

#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
        CObject* PASCAL class_name::CreateObject() \
                { return new class_name; } \
        IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
                class_name::CreateObject, NULL)

怎么是 IMPLEMENT_RUNTIMECLASS ?
原来
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
        IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

他们是等同的。既然内部是使用 DECLARE_DYNAMIC 、IMPLEMENT_DYNAMIC,那么搞清楚他俩,问题也就清楚了。



IMPLEMENT_DYNAMIC是实现“运行时类型识别”宏,与之相对应的是DECLARE_DYNAMIC(声明“运行时类型识别”宏)。也 就是说你在.CPP文件中如果看见有IMPLEMENT_DYNAMIC,则在.H文件中必定有DECLARE_DYNAMIC的声明。

DECLARE_DYNAMIC/DECLARE_DYNAMIC是为了确定运行时对象属于哪一个类而定义的宏。

DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE 的作用:

DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE是为了“动态创建"类的实例而定义的宏。new可以用来创建对象,但不能在还不知道要创建那个类的对象时进行动态的创建。比如说,你要在程序中实现根据拥护输入的类名来创建类的实例,下面的做法是通不过的:
char szClassName[60];
cin >> szClassName;
CObject* pOb=new szClassName; //通不过
这里就要用到DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE定义的功能了。

定义:

//////////////////////////////////////////////////////////////////////////////
// Helper macros for declaring CRuntimeClass compatible classes

#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) /
protected: /
    static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
    static const CRuntimeClass class##class_name; /
    static CRuntimeClass* PASCAL GetThisClass(); /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#define _DECLARE_DYNAMIC(class_name) /
protected: /
    static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
    static CRuntimeClass class##class_name; /
    static CRuntimeClass* PASCAL GetThisClass(); /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#else
#define DECLARE_DYNAMIC(class_name) /
public: /
    static const CRuntimeClass class##class_name; /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#define _DECLARE_DYNAMIC(class_name) /
public: /
    static CRuntimeClass class##class_name; /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#endif


分析:

DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC

#define DECLARE_DYNAMIC(class_name)/   
public:/
    static CRuntimeClass class##class_name;/
    //声明一个类型为CRuntimeClass的静态public成员变量,变量名是由字符串"class"
    //与所指定的类的类名组成。举例而言,如果你写DECLARE_DYNAMIC(CMyView),则等于声明了一个
    // static CRuntimeClass classCMyView静态变量

    virtual CRuntimeClass* GetRuntimeClass() const;/
    //声明一个虚函数,函数名为GetRuntimeClass,返回值为CRuntimeClass类型的指针
    //无参数,并且是个const函数

#define IMPLEMENT_DYNAMIC(class_name,bass_class_name)/
       _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)

#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew)/
    static char _lpsz##class_name[]= #class_name;/
    //定义一个C类型字符串静态变量,变量名由"_lpsz"和指定类的类名组成,变量值为该指定类型的名字
    //比如是CMyView,那么定义的就是static char _lpszCMyView="CMyView";

    CRuntimeClass class_name::class##class_name = {/
        _lpsz##class_name,sizeof(class_name),wSchema,pfnNew,/
            RUNTIME_CLASS(base_class_name),NULL};/
    //给之前在DECLARE_DYNAMIC里定义的CRuntimeClass类型的静态成员变量赋值
    //当然,除最后一个m_pNextClass没有赋值(赋值为NULL,它由下面的结构处理)

    static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);/
    //初始化一个名为"_init_##class_name"的AFX_CLASSINIT静态结构,主要作用是给指定的class_name的
    //class##class_name静态变量的最后一个成员m_pNextClass赋值,具体见下面解释AFX_CLASSINIT中

    CRuntimeClass* class_name::GetRuntimeClass() const/
        { return &class_name::class##class_name;}/
    //之前在DECLARE_DYNAMIC里定义的GetRuntimeClass的实现,很简单,就一个return语句。

#define RUNTIME_CLASS(class_name)/
        (&class_name::class##class_name)
//这部分之所以单独define出一个宏,主要是为了方便从某个指定的class直接得到它的CRuntimeclass静态成员

//以下是解释AFX_CLASSINIT结构,注意,这不是一个宏
//为了看得更加清楚,我按照struct定义的惯常格式来写这个struct的定义
struct AFX_CLASSINIT {
    AFX_CLASSINIT(CRuntimeClass *pNewClass);
};

AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass *pNewClass)
{
    pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
    //让m_pNextClass指向pFirstClass所指的CRuntimeClass变量

    CRuntimeClass::pFirstClass = pNewClass;
    //让pFirstClass指向pNewClass所指的变量,也就是本class的CRuntimeClass静态变量
   //每调用一次就可以将一个新的类的信息加到"链表"中,通过CRuntimeClass::pFirstClass 就可以遍历
}

这样我们就清楚了, 因为 IMPLEMENT_DYNCREATE 只是多了一个创建而已

CObject* PASCAL class_name::CreateObject() \
                { return new class_name; } \

它的本质就是MFC的序列化要求所有类都有一个CreateObject的静态方法用于创建自身。同时每个类都要求维护一个静态的CRuntimeClass对象,用于保存这个类的类型信息以便运行时动态查询。
DECLARE_DYNCREATE这个宏就是使用了一些技巧,来完成以上东西的定义,每个类里面加入这一句宏就可以完成上述的要求而不必自己手动写了。

CRuntimeClass的作用:

CRuntimeClass没有基类。 每个由CObject派生的类都与一个CRuntimeClass结构相联系,用户可以使用该结构获取一个对象及其基类的运行时信息。当需要额外的函数参数检查时,或当用户必须根据一个对象的类编写特殊目的代码时,在运行时确定该对象的类就非常有用。C++并不直接支持运行时类的信息。

在MFC中CObject::IsKindOf( const CRuntimeClass* pClass ) 利用CRuntimeClass来进行判定,如果你生成的类是以CObject为基础的,你可以使用该成员函数来判定。下面举一个例子来加深了解CRuntimeClass,以及那几个宏的作用。

示例代码:

class CAge : public CObject
{
        DECLARE_DYNCREATE(CAge);
};
class CAge2 : public CObject
{
        DECLARE_DYNCREATE(CAge2);
};

IMPLEMENT_DYNCREATE(CAge,CObject)
IMPLEMENT_DYNCREATE(CAge2,CObject)

BOOL IsAge(CObject* pO)
{
        return pO->IsKindOf( RUNTIME_CLASS( CAge ) );
};
BOOL IsAge2(CAge* pO)
{
        return pO->IsKindOf( RUNTIME_CLASS( CAge ) );
};

int main(int argc, char* argv[])
{
        
        CAge age;
        CAge2 age2;
        BOOL bKind = IsAge(&age2);//return FALSE
        bKind = IsAge(&age);//return TRUE
        bKind = IsAge2((CAge*)&age2);//return FALSE,避免强制转换带来的错误
}

感谢:http://blog.sina.com.cn/s/blog_8aa88ea201011xxp.html
的整理,我可以拿你整理的很多现成的信息,哈哈。



原创粉丝点击