3CObject类.docx

上传人:b****5 文档编号:30734893 上传时间:2023-08-20 格式:DOCX 页数:17 大小:28.11KB
下载 相关 举报
3CObject类.docx_第1页
第1页 / 共17页
3CObject类.docx_第2页
第2页 / 共17页
3CObject类.docx_第3页
第3页 / 共17页
3CObject类.docx_第4页
第4页 / 共17页
3CObject类.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

3CObject类.docx

《3CObject类.docx》由会员分享,可在线阅读,更多相关《3CObject类.docx(17页珍藏版)》请在冰豆网上搜索。

3CObject类.docx

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

pClass

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1