FMD(http://www.fmdstudio.net)
MFC CObject浅析
1.CObject简要声明
2.CRuntimeClass结构
3.RUNTIME_CLASS
4.DYNAMIC支持
5.DYNCREATE支持
6.SERIAL支持
CObject是大部分的MFC类的基类,主要是通过它实现:运行类信息、序列化、对象诊断输出、同集合类相兼容、运行时类信息等特殊功能,CObject中添加了特定的处理。为了进一步增强对MFC类对象的理解,在此对CObject源码及相关宏定义进行分析。在此声明中很多都是纯虚函数,定义的一个一般对象的“界面”。
1.CObject简要声明
class CObject{
public:
//返回RUNTIME_CLASS(classObject);
virtualCRuntimeClass* GetRuntimeClass()const;
virtual ~CObject();
void* PASCALoperator new(size_t nSize);
void* PASCALoperator new(size_t, void* p);
void PASCALoperator delete(void* p);
void PASCALoperator delete(void* p, void* pPlace);
#ifdefined(_DEBUG)//调试模式用,多了nLine参数,用于保存原码行号。
oid* PASCALoperator new(size_t nSize, LPCSTR lpszFileName, int
nLine);
void PASCALoperator delete(void *p, LPCSTR lpszFileName, int
nLine);
#endif
protected:
CObject();
private:
CObject(const CObject& objectSrc);
voidoperator=(const CObject& objectSrc);
public:
BOOLIsSerializable() const;
BOOLIsKindOf(const CRuntimeClass* pClass) const;
virtual voidSerialize(CArchive& ar);
#ifdefined(_DEBUG) //调试模式下用
virtual voidAssertValid() const;
virtual voidDump(CDumpContext& dc) const;
#endif
public:
//CObject实现这些功能绝大部分是通过它里面的CRuntimeClass对classObject
//实现的。CObject不支持多重继承,即表示以CObject为基类的类层次中只能
//有一个CObject基类。
//因为CRuntimeClass对象的成员m_pBaseClass的关系。因为它只是一个单链表
static constAFX_DATA CRuntimeClass classCObject;
//此函数返回一个RUNTIME_CLASS
staticCRuntimeClass* PASCAL _GetBaseClass();
};
2.CRuntimeClass结构
在CObject中包含一个静态成员变量:static CRuntimeClassclassCObject。CObject和CRuntimeClass是MFC中两个非常重要的类/结构,绝大部分MFC类都是以CObject做为基类,CRuntimeClass结构同CObject密不可分,了解它们对于深入理解MFC具有重要意义,它是MFC内部用来管理类的重要结构,记录了很多对象所属类的重要信息,通过它在运行时完成对类的管理。很多内部管理成员函数及宏定义都建立在CRuntimeClass的基础上的。每个从CObject中派生的类都有CRuntimeClass对象同它关联完成在运行时得到类实例的信息或者是它的基类。要想使用CRuntimeClass结构得到运行时类信息,必须在你的类中包DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC、
DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE、DECLARE_SERIAL/IMPLEMENT_SERIAL。但你的类必须是从CObject派生的才能使用这些宏。
struct CRuntimeClass{
LPCSTRm_lpszClassName;//类名,一般指包含CRuntimeClass对象的类的名称
intm_nObjectSize;//包含CRuntimeClass对象类sizeof大小,不包括分配内存
UINTm_wSchema; //装载类的版本号
//指向基类CRuntimeClass的指针,用于在运行时记录类继承关系。
CObject*(PASCAL* m_pfnCreateObject)();
#ifdef_AFXDLL
CRuntimeClass* (PASCAL*m_pfnGetBaseClass)();
#else
//m_pBaseClass指针函数是MFC运行时确定类层次关键,它是一个简单单向链表
CRuntimeClass*m_pBaseClass;
#endif
CObject*CreateObject(); //CObject 派生类运行时动态建立的能力
//使用 m_pBaseClass或m_pfnGetBaseClass遍历整个类层次确定是否
//pBaseClass指向的类是基类
//使用它可以判断某类是否是从pBaseClass指向的类在派生来
BOOLIsDerivedFrom(const CRuntimeClass* pBaseClass) const;
voidStore(CArchive& ar) const; //读入
staticCRuntimeClass* PASCAL Load(CArchive& ar, UINT*pwSchemaNum);
CRuntimeClass* m_pNextClass;
};
3.RUNTIME_CLASS
RUNTIME_CLASS(class_name)用于返回指向运行时类信息结构的指针
定义如下:
#defineRUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
即得到类中的CRuntimeClass对象指针,显而易见,如果没有基类你用IMPLEMENT_DYNAMIC时将得到一个编译错误。除非你象CObject一样不用DECLARE_DYNAMIC而定义和实现了这些函数,CObject中的GetBaseClass只是简单的返回NULL。
4.DYNAMIC支持
在CObject派生类中,可以获得动态"验证"支持,访问运行时类信息的
方法:
声明时添加宏:DECLARE_DYNAMIC( class_name)
实现时添加宏: IMPLEMENT_DYNAMIC
原码分析:
// 定义宏
DECLARE_DYNAMIC(class_name)相当于在类中添加如下声明
protected:
staticCRuntimeClass* PASCAL _GetBaseClass();
public:
//静态成员CRuntimeClass,给此派生类添加了运行时类信息,这样就可以使用CRuntimeClass成员判断 //类信息了,此成员名字格式为"class"+"类名",RUNTIME_CLASS()宏就是返回此结构的指针
static constAFX_DATA CRuntimeClass class##class_name;
virtualCRuntimeClass* GetRuntimeClass() const;
// 实现宏
IMPLEMENT_DYNAMIC:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name)
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF,NULL)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name,wSchema, pfnNew)
CRuntimeClass* PASCAL class_name::_GetBaseClass(){
return RUNTIME_CLASS(base_class_name);
} //初始化本类的运行时信息,依次为类名、大小,版本 ,NULL,基类
AFX_COMDATconst AFX_DATADEF CRuntimeClass class_name::class##class_name = {#class_name, sizeof(class class_name), wSchema, pfnNew,&class_name::_GetBaseClass, NULL };
//返回运行时类信息,重载了CObject的GetRuntimeClass
//使得CObject中声明的接口对具体的派生类有效
CRuntimeClass* class_name::GetRuntimeClass()const{
return RUNTIME_CLASS(class_name);
}
有了这些,就可以使用RUNTIME_CLASS()宏,以及用BOOL IsKindOf( constCRuntimeClass* pClass ) const判断类类型了。
5.DYNCREATE支持
类的实例动态生成支持,对于使用DECLARE_DYNCREATE要注意的是类必须要有一个无参数的缺省构造函数。
方法:
添加声明:DECLARE_DYNCREATE( class_name)
添加实现:IMPLEMENT_DYNCREATE( class_name,base_class_name )
原码分析:
DECLARE_DYNCREATE(class_name )
#define DECLARE_DYNCREATE(class_name) //具有DYNAMIC支持DECLARE_DYNAMIC(class_name)
static CObject* PASCAL CreateObject();//用以在动态的建立对象
IMPLEMENT_DYNCREATE(class_name,base_class_name):
#define IMPLEMENT_DYNCREATE(class_name, base_class_name)//动态建立对象
CObject* PASCAL class_name::CreateObject() {
return newclass_name;
}
//填写运行时类信息,与DYNAMIC不同的是,有pfnNew参数
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF,class_name::CreateObject)
6.SERIAL支持
将对象储存,以及读取建立对象支持
方法:
添加声明:DECLARE_SERIAL( class_name)
添加实现:IMPLEMENT_SERIAL( class_name,base_class_name, wSchema)
原码分析
DECLARE_SERIAL( class_name ):
#define DECLARE_SERIAL(class_name) //动态生成支持
_DECLARE_DYNCREATE(class_name) //文档操作符
AFX_APIfriend CArchive& AFXAPI operator>>(CArchive& ar,class_name* &pOb);
IMPLEMENT_SERIAL(class_name, base_class_name,wSchema):
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)//动态生成支持
CObject*PASCAL class_name::CreateObject() {
return new class_name;
}
//填写运行时类信息,包括版本号,生成函数指针
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema,class_name::CreateObject) AFX_CLASSINIT_init_##class_name(RUNTIME_CLASS(class_name)); //文档支持实现
CArchive& AFXAPI operator>>(CArchive& ar, class_name*&pOb) {
pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name));
return ar;
}
在派生类中重载virtual void Serialize(CArchive&ar);以实现类数据的保存及建立后读入。
第一级的宏是DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC,它允许你在运行时处理类名和类层次中的位置,允许你做有意义的诊断Dump。
第二级的宏是DECLARE_SERIAL/IMPLEMENT_SERIAL,它包括第一级宏所有的功能,允许你进行对象的序列化。
DECLARE_DYNAMIC定义了运行时类对象,而IMPLEMENT_DYNAMIC是实现,thatlooks like as below:
class CObject{
staticCRuntimeClass m_object; // 定义在DECLARE_DYNAMIC
};
//Implementations
CRuntimeClass CObject::m_object; //实现在IMPLEMENT_DYNAMIC
CObject作为根,它是不应该存在运行时对象m_object的,可以用NULL表示CObject,这样在指针链表中比较方便。声明和实现是一对的,没有声明就没实现。