篆体字网 > 知识库

implement_dyncreate

来源:篆体字网 2024-01-03 17:36:06 作者:篆字君

MFC序列化机制中的一个潜在的错误MFC在IMPLEMENT_SERIAL使用VERSIONABLE_SCHEMA|schema number来提供文件格式的向后兼容.即新的程序既可以读新的文件格式,也可以读较老的文件格式.当它发现要被反序列化的CObject派生类对象内容的shema和程序该类的schema不一致时,如果该类是VERSIONABLE_SCHEMA的(这个信息记录在该类所对应的CRuntimeClass中),那么它仍然调用该类的序列化函数;否则终止反序列化过程,抛出CArchiveException异常,最终向用户报告文件格式错误.如果一个类是VERSIONABLE_SCHEMA的,那么该类的反序列化必须根据不同的schma number来进行.就在这里,微软犯了一个潜在的错误,微软对于一个对象只保存一个schema number[对象所属类的],这是不够的.因为一个对象从CObject到对象的实际类型可能不止一级,而每一级的schema number都有变化的可能.当中间层次的类发生变化时,VERSIONABLE_SCHEMA机制就失效了.解决这个问题的办法就是自己在每个类层次里序列化类的版本号,根据该版本号进行反序列化,完全忽略MFC的VERSIONABLE_SCHEMA机制.当然,这个问题不可能在MFC框架内得到解决,我觉得MFC开发小组可以有两个办法:一是完全让用户负责文件的向后兼容;一是在MSDN里说明这个潜在的问题,引起程序员的注意.------------------------------------------------------------------------------------------------------MFC源码:#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; \// 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();#define DECLARE_SERIAL(class_name) \_DECLARE_DYNCREATE(class_name) \AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \AFX_COMDAT const CRuntimeClass class_name::class##class_name={ \#class_name, sizeof(class class_name), wSchema, pfnNew, \RUNTIME_CLASS(base_class_name), NULL, class_init }; \CRuntimeClass* class_name::GetRuntimeClass() const \{ return RUNTIME_CLASS(class_name); }#define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \AFX_COMDAT CRuntimeClass class_name::class##class_name={ \#class_name, sizeof(class class_name), wSchema, pfnNew, \RUNTIME_CLASS(base_class_name), NULL, class_init }; \CRuntimeClass* class_name::GetRuntimeClass() const \{ return RUNTIME_CLASS(class_name); }#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)#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)#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \CObject* PASCAL class_name::CreateObject() \{ return new class_name; } \extern AFX_CLASSINIT _init_##class_name; \_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \class_name::CreateObject, &_init_##class_name) \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; }以下是我(fyl)对这些宏的理解:IMPLEMENT_RUNTIMECLASS中定义了静态CRuntimeClass对象,并赋予当前类与基类的信息 ,当前类类名及版本号wSchema,动态创建当前类对象的函数地址CreateObject等。它实现了基类与派生类之间的连接,在MFC类层次之间形成了树形层次的结构,所以可以在此基础上实现动态类 型的识别IsKindOf:while (pClassThis !=NULL){if (pClassThis==pBaseClass)return TRUE;if (pClassThis->m_pfnGetBaseClass==NULL)return FALSE;pClassThis=(*pClassThis->m_pfnGetBaseClass)();}DECLARE_DYNAMIC仅提供动态类型的识别,只需要基类与派生类之间的连接,不需要其它信息,所以IMPLEMENT_DYNAMIC调用的IMPLEMENT_RUNTIMECLASS版本号:0xFFFF(表示无效), 动态创建当前类对象的函数地址:NULL。DECLARE_DYNCREATE增加了动态创建,所以IMPLEMENT_DYNCREATE增加了动态创建当前类对象的函数CreateObject,并传递给IMPLEMENT_RUNTIMECLASS。DECLARE_SERIAL实现串行化(保存与读取),不同版本的保存与读取是可能存在不同的,所以需要版本号,所以IMPLEMENT_SERIAL增加了版本号传递;另外,在读取时,读到class信息时需要判断是那个类后才可以动态生成,所以IMPLEMENT_SERIAL中增加了AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name))。AFX_CLASSINIT是结构的定义:struct AFX_CLASSINIT{ AFX_CLASSINIT(CRuntimeClass* pNewClass); };仅有一个构造函数,定义如下:AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass){pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;CRuntimeClass::pFirstClass=pNewClass;}此构造函数负责 linked list 的串接工作。在MFC类中依靠AFX_CLASSINIT在原有树形的结构上增加了链表,链表将所有包含*_SERIAL对的MFC类串接后形成一个表。所以读到class信息时在此链表查询即可!问题1(主要问题):*_SERIAL中增加了friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);但我没有使用它时也可以串行化!CTrip是可串行化的类.操作如下:头文件中用_DECLARE_DYNCREATE(CTrip)替换DECLARE_SERIAL源文件中用CObject* PASCAL CTrip::CreateObject() \{ return new CTrip; } \_IMPLEMENT_RUNTIMECLASS(CTrip, CObject, 2, \CTrip::CreateObject, NULL)\AFX_CLASSINIT _init_CTrip(RUNTIME_CLASS(CTrip));替换IMPLEMENT_SERIAL!替换后CTrip同样可以完成串行化的工作?在源文件中增加CArchive& AFXAPI operator>>(CArchive& ar, CTrip* &pOb) \{ pOb=(CTrip*) ar.ReadObject(RUNTIME_CLASS(CTrip)); \return ar; },并在此处设置断点,但是始终不进入,说明没有用到?问题2:struct CRuntimeClass{

上一篇:百度知道留链接

下一篇:2014年终总结

相关阅读