📄 mfc3.php
字号:
<P align=justify>CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CPerson)</P>
<P align=justify>//Cperson有一个缺省构造函数</P>
<P align=justify>CObject* pObject = pRuntimeClass->CreateObject();</P>
<P align=justify>Assert( pObject->IsKindOf(RUNTIME_CLASS(CPerson)); </P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445888993></A><A name=_Toc445782396></A><A
name=_Toc452640892></A><A name=_Toc457298957></A><B>实现CObject特性的机制</B>
<P></P>
<P
align=justify>由上,清楚了CObject的结构,也清楚了从CObject派生新类时程序员使用CObject特性的方法。现在来考察这些方法如何利用CObjet的结构,CObject结构如何支持这些方法。</P>
<P align=justify>首先,要揭示DECLARE_DYNAMIC等宏的内容,然后,分析这些宏的作用。</P>
<OL>
<P align=justify>
<LI><A name=_Toc445888994></A><A name=_Toc445782397></A><A
name=_Toc452640893></A><A
name=_Toc457298958></A><B>DECLARE_DYNAMIC等宏的定义</B>
<P></P></LI></OL></LI></OL></OL>
<P
align=justify>MFC提供了DECLARE_DYNAMIC、DECLARE_DYNCREATE、DECLARE_SERIAL声明宏的两种定义,分别用于静态链接到MFC
DLL和动态链接到MFC DLL。对应的实现宏IMPLEMNET_XXXX也有两种定义,但是,这里实现宏就不列举了。</P>
<P align=justify></P>
<P align=justify>MFC对这些宏的定义如下:</P>
<P align=justify>#ifdef _AFXDLL //动态链接到MFC DLL</P>
<P align=justify>#define <B>DECLARE_DYNAMIC</B>(class_name) \</P>
<P align=justify>protected: \</P>
<P align=justify>static CRuntimeClass* PASCAL _GetBaseClass(); \</P>
<P align=justify>public: \</P>
<P align=justify>static const AFX_DATA CRuntimeClass class##class_name; \</P>
<P align=justify>virtual CRuntimeClass* GetRuntimeClass() const; \</P>
<P align=justify></P>
<P align=justify>#define _<B>DECLARE_DYNAMIC</B>(class_name) \</P>
<P align=justify>protected: \</P>
<P align=justify>static CRuntimeClass* PASCAL _GetBaseClass(); \</P>
<P align=justify>public: \</P>
<P align=justify>static AFX_DATA CRuntimeClass class##class_name; \</P>
<P align=justify>virtual CRuntimeClass* GetRuntimeClass() const; \</P>
<P align=justify></P>
<P align=justify>#else</P>
<P align=justify>#define <B>DECLARE_DYNAMIC</B>(class_name) \</P>
<P align=justify>public: \</P>
<P align=justify>static const AFX_DATA CRuntimeClass class##class_name; \</P>
<P align=justify>virtual CRuntimeClass* GetRuntimeClass() const; \</P>
<P align=justify></P>
<P align=justify>#define <B>_DECLARE_DYNAMIC</B>(class_name) \</P>
<P align=justify>public: \</P>
<P align=justify>static AFX_DATA CRuntimeClass class##class_name; \</P>
<P align=justify>virtual CRuntimeClass* GetRuntimeClass() const; \</P>
<P align=justify></P>
<P align=justify>#endif</P>
<P align=justify></P>
<P align=justify>// not serializable, but dynamically constructable</P>
<P align=justify>#define <B>DECLARE_DYNCREATE</B>(class_name) \</P>
<P align=justify>DECLARE_DYNAMIC(class_name) \</P>
<P align=justify>static CObject* PASCAL CreateObject();</P>
<P align=justify></P>
<P align=justify>#define <B>DECLARE_SERIAL</B>(class_name) \</P>
<P align=justify>_DECLARE_DYNCREATE(class_name) \</P>
<P align=justify>friend CArchive& AFXAPI operator>>(CArchive& ar,
class_name* &pOb);</P>
<P align=justify></P>
<P
align=justify>由于这些声明宏都是在CObect派生类的定义中被使用的,所以从这些宏的上述定义中可以看出,DECLARE_DYNAMIC宏给所在类添加了一个CRuntimeClass类型的静态数据成员class##class_name(类名加前缀class,例如,若类名是CPerson,则该变量名称是classCPerson),且指定为const;两个(使用MFC
DLL时,否则,一个)成员函数:虚拟函数GetRuntimeClass和静态函数_GetBaseClass(使用MFC DLL时)。</P>
<P
align=justify>DECLARE_DYNCREATE宏包含了DECLARE_DYNAMIC,在此基础上,还定义了一个静态成员函数CreateObject。</P>
<P
align=justify>DECLARE_SERIAL宏则包含了_DECLARE_DYNCREATE,并重载了操作符“>>”(友员函数)。它和前两个宏有所不同的是CRuntimeClass数据成员class##class_name没有被指定为const。</P>
<P align=justify></P>
<P
align=justify>对应地,MFC使用三个宏初始化DECLARE宏所定义的静态变量并实现DECLARE宏所声明的函数:IMPLEMNET_DYNAMIC,IMPLEMNET_DYNCREATE,IMPLEMENT_SERIAL。</P>
<P
align=justify>首先,这三个宏初始化CRuntimeClass类型的静态成员变量class#class_name。IMPLEMENT_SERIAL不同于其他两个宏,没有指定该变量为const。初始化内容在下节讨论CRuntimeClass时给出。</P>
<P align=justify>其次,它实现了DECLARE宏声明的成员函数:</P>
<UL>
<P align=justify>
<LI>_GetBaseClass()
<P></P></LI></UL>
<P align=justify>返回基类的运行时类信息,即基类的CRuntimeClass类型的静态成员变量。这是静态成员函数。</P>
<UL>
<P align=justify>
<LI>GetRuntimeClass()
<P></P></LI></UL>
<P align=justify>返回类自己的运行类信息,即其CRuntimeClass类型的静态成员变量。这是虚拟成员函数。</P>
<P
align=justify>对于动态创建宏,还有一个静态成员函数CreateObject,它使用C++操作符和类的缺省构造函数创建本类的一个动态对象。</P>
<UL>
<P align=justify>
<LI>操作符的重载
<P></P></LI></UL>
<P align=justify>对于序列化的实现宏IMPLEMENT_SERIAL,还重载了操作符<<和定义了一个静态成员变量</P>
<P align=justify>static const AFX_CLASSINIT
_init_##class_name(RUNTIME_CLASS(class_name)); </P>
<P
align=justify>比如,对CPerson来说,该变量是_init_Cperson,其目的在于静态成员在应用程序启动之前被初始化,使得AFX_CLASSINIT类的构造函数被调用,从而通过AFX_CLASSINIT类的构造函数在模块状态的CRuntimeClass链表中插入构造函数参数表示的CRuntimeClass类信息。至于模块状态,在后文有详细的讨论。</P>
<P align=justify>重载的操作符函数用来在序列化时从文档中读入该类对象的内容,是一个友员函数。定义如下:</P>
<P align=justify>CArchive& AFXAPI operator>>(CArchive& ar,
class_name* &pOb) </P>
<P align=justify>{</P>
<P align=justify>pOb = (class_name*) ar.ReadObject(</P>
<P align=justify>RUNTIME_CLASS(class_name));</P>
<P align=justify>return ar;</P>
<P align=justify>}</P>
<P
align=justify>回顾CObject的定义,它也有一个CRuntimeClass类型的静态成员变量classCObject,因为它本身也支持三个特性。</P>
<P
align=justify>以CObject及其派生类的静态成员变量classCObject为基础,IsKindOf和动态创建等函数才可以起到作用。</P>
<P
align=justify>这个变量为什么能有这样的用处,这就要分析CRuntimeClass类型变量的结构和内容了。下面,在讨论了CRuntimeClass的结构之后,考察该类型的静态变量被不同的宏初始化之后的内容。</P>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445888995></A><A name=_Toc445782398></A><A
name=_Toc452640894></A><A
name=_Toc457298959></A><B>CruntimeClass类的结构与功能</B>
<P></P></LI></OL></OL></OL>
<P
align=justify>从上面的讨论可以看出,在对CObject特性的支持上,CRuntimeClass类起到了关键作用。下面,考查它的结构和功能。</P>
<OL>
<P align=justify>
<LI>CRuntimeClass的结构
<P></P>
<P align=justify>CruntimeClass的结构如下:</P>
<P align=justify>Struct CRuntimeClass</P>
<P align=justify>{</P>
<P align=justify>LPCSTR m_lpszClassName;//类的名字</P>
<P align=justify>int m_nObjectSize;//类的大小</P>
<P align=justify>UINT m_wSchema;</P>
<P align=justify>CObject* (PASCAL* m_pfnCreateObject)();</P>
<P align=justify>//pointer to function, equal to newclass.CreateObject() </P>
<P align=justify>//after IMPLEMENT</P>
<P align=justify>CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();</P>
<P align=justify>CRumtieClass* m_pBaseClass;</P>
<P align=justify></P>
<P align=justify>//operator:</P>
<P align=justify>CObject *CreateObject();</P>
<P align=justify>BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass)
const;</P>
<P align=justify>...</P>
<P align=justify>}</P>
<P
align=justify>CRuntimeClass成员变量中有两个是函数指针,还有几个用来保存所在CruntimeClass对象所在类的名字、类的大小(字节数)等。</P>
<P align=justify>这些成员变量被三个实现宏初始化,例如:</P>
<P
align=justify>m_pfnCreateObject,将被初始化指向所在类的静态成员函数CreateObject。CreateObject函数在初始化时由实现宏定义,见上文的说明。</P>
<P
align=justify>m_pfnGetBaseClass,如果定义了_AFXDLL,则该变量将被初始化指向所在类的成员函数_GetBaseClass。_GetBaseClass在声明宏中声明,在初始化时由实现宏定义,见上文的说明。</P>
<P
align=justify>下面,分析三个宏对CObject及其派生类的CRuntimeClass类型的成员变量class##class_name初始化的情况,然后讨论CRuntimeClass成员函数的实现。</P>
<P align=justify></P>
<LI>成员变量class##class_name的内容
<P></P>
<P
align=justify>IMPLEMENT_DYNCREATE等宏将初始化类的CRuntimeClass类型静态成员变量的各个域,表3-1列出了在动态类信息、动态创建、序列化这三个不同层次下对该静态成员变量的初始化情况:</P>
<P align=center>表3-1 静态成员变量class##class_name的初始化</P>
<TABLE cellSpacing=1 cellPadding=7 width=497 border=1>
<TBODY>
<TR>
<TD vAlign=top width="27%">
<P align=justify>CRuntimeClass成员变量 </P></TD>
<TD vAlign=top width="24%">
<P align=justify>动态类信息 </P></TD>
<TD vAlign=top width="25%">
<P align=justify>动态创建 </P></TD>
<TD vAlign=top width="24%">
<P align=justify>序列化 </P></TD></TR>
<TR>
<TD vAlign=top width="27%">
<P align=justify>m_lpszClassName </P></TD>
<TD vAlign=top width="24%">
<P align=justify>类名字符串 </P></TD>
<TD vAlign=top width="25%">
<P align=justify>类名字符串 </P></TD>
<TD vAlign=top width="24%">
<P align=justify>类名字符串 </P></TD></TR>
<TR>
<TD vAlign=top width="27%">
<P align=justify>m_nObjectSize </P></TD>
<TD vAlign=top width="24%">
<P align=justify>类的大小(字节数) </P></TD>
<TD vAlign=top width="25%">
<P align=justify>类的大小(字节数) </P></TD>
<TD vAlign=top width="24%">
<P align=justify>类的大小(字节数) </P></TD></TR>
<TR>
<TD vAlign=top width="27%">
<P align=justify>m_wShema </P></TD>
<TD vAlign=top width="24%">
<P align=justify>0xFFFF </P></TD>
<TD vAlign=top width="25%">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -