3CObject类.docx
- 文档编号:9696768
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:17
- 大小:30.88KB
3CObject类.docx
《3CObject类.docx》由会员分享,可在线阅读,更多相关《3CObject类.docx(17页珍藏版)》请在冰豆网上搜索。
3CObject类
3.CObject类
CObject是大多数MFC类的根类或基类。
CObject类有很多有用的特性:
对运行时类信息的支持,对动态创建的支持,对串行化的支持,对象诊断输出,等等。
MFC从CObject派生出许多类,具备其中的一个或者多个特性。
程序员也可以从CObject类派生出自己的类,利用CObject类的这些特性。
本章将讨论MFC如何设计CObject类的这些特性。
首先,考察CObject类的定义,分析其结构和方法(成员变量和成员函数)对CObject特性的支持。
然后,讨论CObject特性及其实现机制。
1.CObject的结构
以下是CObject类的定义:
classCObject
{
public:
//与动态创建相关的函数
virtualCRuntimeClass*GetRuntimeClass()const;
析构函数
virtual~CObject();//virtualdestructorsarenecessary
//与构造函数相关的内存分配函数,可以用于DEBUG下输出诊断信息
void*PASCALoperatornew(size_tnSize);
void*PASCALoperatornew(size_t,void*p);
voidPASCALoperatordelete(void*p);
#ifdefined(_DEBUG)&&!
defined(_AFX_NO_DEBUG_CRT)
void*PASCALoperatornew(size_tnSize,LPCSTRlpszFileName,intnLine);
#endif
//缺省情况下,复制构造函数和赋值构造函数是不可用的
//如果程序员通过传值或者赋值来传递对象,将得到一个编译错误
protected:
//缺省构造函数
CObject();
private:
//复制构造函数,私有
CObject(constCObject&objectSrc);//noimplementation
//赋值构造函数,私有
voidoperator=(constCObject&objectSrc);//noimplementation
//Attributes
public:
//与运行时类信息、串行化相关的函数
BOOLIsSerializable()const;
BOOLIsKindOf(constCRuntimeClass*pClass)const;
//Overridables
virtualvoidSerialize(CArchive&ar);
//诊断函数
virtualvoidAssertValid()const;
virtualvoidDump(CDumpContext&dc)const;
//Implementation
public:
//与动态创建对象相关的函数
staticconstAFX_DATACRuntimeClassclassCObject;
#ifdef_AFXDLL
staticCRuntimeClass*PASCAL_GetBaseClass();
#endif
};
由上可以看出,CObject定义了一个CRuntimeClass类型的静态成员变量:
CRuntimeClassclassCObject
还定义了几组函数:
构造函数析构函数类,
诊断函数,
与运行时类信息相关的函数,
与串行化相关的函数。
其中,一个静态函数:
_GetBaseClass;五个虚拟函数:
析构函数、GetRuntimeClass、Serialize、AssertValid、Dump。
这些虚拟函数,在CObject的派生类中应该有更具体的实现。
必要的话,派生类实现它们时可能要求先调用基类的实现,例如Serialize和Dump就要求这样。
静态成员变量classCObject和相关函数实现了对CObjet特性的支持。
2.CObject类的特性
下面,对三种特性分别描述,并说明程序员在派生类中支持这些特性的方法。
1.对运行时类信息的支持
该特性用于在运行时确定一个对象是否属于一特定类(是该类的实例),或者从一个特定类派生来的。
CObject提供IsKindOf函数来实现这个功能。
从CObject派生的类要具有这样的特性,需要:
∙定义该类时,在类说明中使用DECLARE_DYNAMIC(CLASSNMAE)宏;
∙在类的实现文件中使用IMPLEMENT_DYNAMIC(CLASSNAME,BASECLASS)宏。
1.对动态创建的支持
前面提到了动态创建的概念,就是运行时创建指定类的实例。
在MFC中大量使用,如前所述框架窗口对象、视对象,还有文档对象都需要由文档模板类(CDocTemplate)对象来动态的创建。
从CObject派生的类要具有动态创建的功能,需要:
∙定义该类时,在类说明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;
∙定义一个不带参数的构造函数(默认构造函数);
∙在类的实现文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏;
∙使用时先通过宏RUNTIME_CLASS得到类的RunTime信息,然后使用CRuntimeClass的成员函数CreateObject创建一个该类的实例。
例如:
CRuntimeClass*pRuntimeClass=RUNTIME_CLASS(CNname)
//CName必须有一个缺省构造函数
CObject*pObject=pRuntimeClass->CreateObject();
//用IsKindOf检测是否是CName类的实例
Assert(pObject->IsKindOf(RUNTIME_CLASS(CName));
1.对序列化的支持
“序列化”就是把对象内容存入一个文件或从一个文件中读取对象内容的过程。
从CObject派生的类要具有序列化的功能,需要:
∙定义该类时,在类说明中使用DECLARE_SERIAL(CLASSNMAE)宏;
∙定义一个不带参数的构造函数(默认构造函数);
∙在类的实现文件中使用IMPLEMENT_SERIAL(CLASSNAME,BASECLASS)宏;
∙覆盖Serialize成员函数。
(如果直接调用Serialize函数进行序列化读写,可以省略前面三步。
)
对运行时类信息的支持、动态创建的支持、串行化的支持层(不包括直接调用Serailize实现序列化),这三种功能的层次依次升高。
如果对后面的功能支持,必定对前面的功能支持。
支持动态创建的话,必定支持运行时类信息;支持序列化,必定支持前面的两个功能,因为它们的声明和实现都是后者包含前者。
1.综合示例:
定义一个支持串行化的类CPerson:
classCPerson:
publicCObject
{
public:
DECLARE_SERIAL(CPerson)
//缺省构造函数
CPerson(){}{};
CStringm_name;
WORDm_number;
voidSerialize(CArchive&archive);
//restofclassdeclaration
};
实现该类的成员函数Serialize,覆盖CObject的该函数:
voidCPerson:
:
Serialize(CArchive&archive)
{
//先调用基类函数的实现
CObject:
:
Serialize(archive);
//nowdothestuffforourspecificclass
if(archive.IsStoring())
archive< else archive>>m_name>>m_number; } 使用运行时类信息: CPersona; ASSERT(a.IsKindOf(RUNTIME_CLASS(CPerson))); ASSERT(a.IsKindOf(RUNTIME_CLASS(CObject))); 动态创建: CRuntimeClass*pRuntimeClass=RUNTIME_CLASS(CPerson) //Cperson有一个缺省构造函数 CObject*pObject=pRuntimeClass->CreateObject(); Assert(pObject->IsKindOf(RUNTIME_CLASS(CPerson)); 1.实现CObject特性的机制 由上,清楚了CObject的结构,也清楚了从CObject派生新类时程序员使用CObject特性的方法。 现在来考察这些方法如何利用CObjet的结构,CObject结构如何支持这些方法。 首先,要揭示DECLARE_DYNAMIC等宏的内容,然后,分析这些宏的作用。 1.DECLARE_DYNAMIC等宏的定义 MFC提供了DECLARE_DYNAMIC、DECLARE_DYNCREATE、DECLARE_SERIAL声明宏的两种定义,分别用于静态链接到MFCDLL和动态链接到MFCDLL。 对应的实现宏IMPLEMNET_XXXX也有两种定义,但是,这里实现宏就不列举了。 MFC对这些宏的定义如下: #ifdef_AFXDLL//动态链接到MFCDLL #defineDECLARE_DYNAMIC(class_name)\ protected: \ staticCRuntimeClass*PASCAL_GetBaseClass();\ public: \ staticconstAFX_DATACRuntimeClassclass##class_name;\ virtualCRuntimeClass*GetRuntimeClass()const;\ #define_DECLARE_DYNAMIC(class_name)\ protected: \ staticCRuntimeClass*PASCAL_GetBaseClass();\ public: \ staticAFX_DATACRuntimeClassclass##class_name;\ virtualCRuntimeClass*GetRuntimeClass()const;\ #else #defineDECLARE_DYNAMIC(class_name)\ public: \ staticconstAFX_DATACRuntimeClassclass##class_name;\ virtualCRuntimeClass*GetRuntimeClass()const;\ #define_DECLARE_DYNAMIC(class_name)\ public: \ staticAFX_DATACRuntimeClassclass##class_name;\ virtualCRuntimeClass*GetRuntimeClass()const;\ #endif //notserializable,butdynamicallyconstructable #defineDECLARE_DYNCREATE(class_name)\ DECLARE_DYNAMIC(class_name)\ staticCObject*PASCALCreateObject(); #defineDECLARE_SERIAL(class_name)\ _DECLARE_DYNCREATE(class_name)\ friendCArchive&AFXAPIoperator>>(CArchive&ar,class_name*&pOb); 由于这些声明宏都是在CObect派生类的定义中被使用的,所以从这些宏的上述定义中可以看出,DECLARE_DYNAMIC宏给所在类添加了一个CRuntimeClass类型的静态数据成员class##class_name(类名加前缀class,例如,若类名是CPerson,则该变量名称是classCPerson),且指定为const;两个(使用MFCDLL时,否则,一个)成员函数: 虚拟函数GetRuntimeClass和静态函数_GetBaseClass(使用MFCDLL时)。 DECLARE_DYNCREATE宏包含了DECLARE_DYNAMIC,在此基础上,还定义了一个静态成员函数CreateObject。 DECLARE_SERIAL宏则包含了_DECLARE_DYNCREATE,并重载了操作符“>>”(友员函数)。 它和前两个宏有所不同的是CRuntimeClass数据成员class##class_name没有被指定为const。 对应地,MFC使用三个宏初始化DECLARE宏所定义的静态变量并实现DECLARE宏所声明的函数: IMPLEMNET_DYNAMIC,IMPLEMNET_DYNCREATE,IMPLEMENT_SERIAL。 首先,这三个宏初始化CRuntimeClass类型的静态成员变量class#class_name。 IMPLEMENT_SERIAL不同于其他两个宏,没有指定该变量为const。 初始化内容在下节讨论CRuntimeClass时给出。 其次,它实现了DECLARE宏声明的成员函数: ∙_GetBaseClass() 返回基类的运行时类信息,即基类的CRuntimeClass类型的静态成员变量。 这是静态成员函数。 ∙GetRuntimeClass() 返回类自己的运行类信息,即其CRuntimeClass类型的静态成员变量。 这是虚拟成员函数。 对于动态创建宏,还有一个静态成员函数CreateObject,它使用C++操作符和类的缺省构造函数创建本类的一个动态对象。 ∙操作符的重载 对于序列化的实现宏IMPLEMENT_SERIAL,还重载了操作符<<和定义了一个静态成员变量 staticconstAFX_CLASSINIT_init_##class_name(RUNTIME_CLASS(class_name)); 比如,对CPerson来说,该变量是_init_Cperson,其目的在于静态成员在应用程序启动之前被初始化,使得AFX_CLASSINIT类的构造函数被调用,从而通过AFX_CLASSINIT类的构造函数在模块状态的CRuntimeClass链表中插入构造函数参数表示的CRuntimeClass类信息。 至于模块状态,在后文有详细的讨论。 重载的操作符函数用来在序列化时从文档中读入该类对象的内容,是一个友员函数。 定义如下: CArchive&AFXAPIoperator>>(CArchive&ar,class_name*&pOb) { pOb=(class_name*)ar.ReadObject( RUNTIME_CLASS(class_name)); returnar; } 回顾CObject的定义,它也有一个CRuntimeClass类型的静态成员变量classCObject,因为它本身也支持三个特性。 以CObject及其派生类的静态成员变量classCObject为基础,IsKindOf和动态创建等函数才可以起到作用。 这个变量为什么能有这样的用处,这就要分析CRuntimeClass类型变量的结构和内容了。 下面,在讨论了CRuntimeClass的结构之后,考察该类型的静态变量被不同的宏初始化之后的内容。 1.CruntimeClass类的结构与功能 从上面的讨论可以看出,在对CObject特性的支持上,CRuntimeClass类起到了关键作用。 下面,考查它的结构和功能。 1.CRuntimeClass的结构 CruntimeClass的结构如下: StructCRuntimeClass { LPCSTRm_lpszClassName;//类的名字 intm_nObjectSize;//类的大小 UINTm_wSchema; CObject*(PASCAL*m_pfnCreateObject)(); //pointertofunction,equaltonewclass.CreateObject() //afterIMPLEMENT CRuntimeClass*(PASCAL*m_pfnGetBaseClass)(); CRumtieClass*m_pBaseClass; //operator: CObject*CreateObject(); BOOLIsDerivedFrom(constCRuntimeClass*pBaseClass)const; ... } CRuntimeClass成员变量中有两个是函数指针,还有几个用来保存所在CruntimeClass对象所在类的名字、类的大小(字节数)等。 这些成员变量被三个实现宏初始化,例如: m_pfnCreateObject,将被初始化指向所在类的静态成员函数CreateObject。 CreateObject函数在初始化时由实现宏定义,见上文的说明。 m_pfnGetBaseClass,如果定义了_AFXDLL,则该变量将被初始化指向所在类的成员函数_GetBaseClass。 _GetBaseClass在声明宏中声明,在初始化时由实现宏定义,见上文的说明。 下面,分析三个宏对CObject及其派生类的CRuntimeClass类型的成员变量class##class_name初始化的情况,然后讨论CRuntimeClass成员函数的实现。 2.成员变量class##class_name的内容 IMPLEMENT_DYNCREATE等宏将初始化类的CRuntimeClass类型静态成员变量的各个域,表3-1列出了在动态类信息、动态创建、序列化这三个不同层次下对该静态成员变量的初始化情况: 表3-1静态成员变量class##class_name的初始化 CRuntimeClass成员变量 动态类信息 动态创建 序列化 m_lpszClassName 类名字符串 类名字符串 类名字符串 m_nObjectSize 类的大小(字节数) 类的大小(字节数) 类的大小(字节数) m_wShema 0xFFFF 0xFFFF 1、2等,非0 m_pfnCreateObject NULL 类的成员函数 CreateObject 类的成员函数 CreateObject m_pBaseClass 基类的CRuntimeClass变量 基类的CRuntimeClass变量 基类的CRuntimeClass变量 m_pfnGetBaseClass 类的成员函数 _GetBaseClass 类的成员函数 _GetBaseClass 类的成员函数 _GetBaseClass m_pNextClass NULL NULL NULL m_wSchema类型是UINT,定义了序列化中保存对象到文档的程序的版本。 如果不要求支持序列化特性,该域为0XFFFF,否则,不能为0。 Cobject类本身的静态成员变量classCObject被初始化为: {"CObject",sizeof(CObject),0xffff,NULL,&CObject: : _GetBaseClass,NULL}; 对初始化内容解释如下: 类名字符串是“CObject”,类的大小是sizeof(CObject),不要求支持序列化,不支持动态创建。 3.成员函数CreateObject 回顾3.2节,动态创建对象是通过语句pRuntimeClass->CreateObject完成的,即调用了CRuntimeClass自己的成员函数,CreateObject函数又调用m_pfnCreateObject指向的函数来完成动态创建任务,如下所示: CObject*CRuntimeClass: : CreateObject() { if(m_pfnCreateObject==NULL)//判断函数指针是否空 { TRACE(_T("Error: Tryingtocreateobjectwhichisnot") _T("DECLARE_DYNCREATE\norDECLARE_SERIAL: %hs.\n"), m_lpszClassName); returnNULL; } //函数指针非空,继续处理 CObject*pObject=NULL; TRY { pObject=(*m_pfnCreateObject)();//动态创建对象 } END_TRY returnpObject; } 4.成员函数IsDerivedFrom 该函数用来帮助运行时判定一个类是否派生于另一个类,被CObject的成员函数IsKindOf函数所调用。 其实现描述如下: 如果定义了_AFXDLL则,成员函数IsDerivedFrom调用成员函数m_pfnGetBaseClass指向的函数来向上逐层得到基类的CRuntimeClass类型的静态成员变量,直到某个基类的CRuntimeClass类型的静态成员变量和参数指定的CRuntimeClass变量一致或者追寻到最上层为止。 如果没有定义_AFXDLL,则使用成员变量m_pBaseClass基类的CRuntimeClass类型的静态成员变量。 程序如下所示: BOOLCRuntimeClass: : IsDerivedFrom( constCRuntimeClass*pBaseClass)const { ASSERT(this! =NULL); ASSERT(AfxIsValidAddress(this,sizeof(CRuntimeClass),FALSE)); ASSERT(pBaseClass! =NULL); ASSERT(AfxIsValidAddress(pBaseClass,sizeof(CRuntimeClass),FALSE)); //simpleSIcase constCRuntimeClass*pClassThis=this; while(pClassThis! =NULL)//从本类开始向上逐个基类搜索 { if(pClassThis==pBaseClass)//若是参数指定的类信息 returnTRUE; //类信息不符合,继续向基类搜索 #ifdef_AFXDLL pClassThis=(*pClassThis->m_pfnGetBaseClass)(); #else
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- CObject