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
{

解决方案 »

  1.   


    问题2:
    struct CRuntimeClass
    {
    ........
    const AFX_CLASSINIT* m_pClassInit;//有什么用处?
    };
      

  2.   

    之前就做了:设置断点调试,用F11可以跟踪函数调用的全过程!
    调用的_AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb)
    { pOb = ar.ReadObject(NULL); return ar; }
    而非CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb); 
      

  3.   

    CTrip的定义和执行串行化的代码具体是怎么写的?
      

  4.   

    不需要关心CTripCTrip的定义和执行串行化的代码具体是怎么写
    它是按照MSDN中Serialization: Making a Serializable Class 的步骤实现的a Serializable Class :
    串行化的代码可假设Serialize 一个整数int a;可编程实践!
      

  5.   


    const AFX_CLASSINIT* m_pClassInit; // 用于建立类别型录// Basic object model
     3
     4    // generate static object constructor for class registration
     5    void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);
     6    struct AFX_CLASSINIT
     7        { AFX_CLASSINIT(CRuntimeClass* pNewClass) { AfxClassInit(pNewClass); } };
     8    //C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\objcore.cpp Line157
     9    void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)
    10    {
    11        AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    12        AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
    13        pModuleState->m_classList.AddHead(pNewClass);
    14        AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
    15    }
    16    //可以将AfxClassInit()函数的功能简单的如下表示:
    17    AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)
    18    {
    19        pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
    20        CRuntimeClass::pFirstClass = pNewClass;
    21    }
      

  6.   

    [Quote=引用 11 楼 oyljerry 的回复:]
    sorry,我没有说清楚,我明白它的作用,问题应该是为什么在CRuntimeClass中添加AFX_CLASSINIT* m_pClassInit,
    还把AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name))的对象地址给m_pClassInit; 不用它不也可以吗?
      

  7.   

    重新描述: 
    问题等同http://topic.csdn.net/u/20090207/10/2e284d86-1a02-4689-b8b1-f962fccd19bd.html
    ,解决后俩个加在一起给予200分问题:不用AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb), m_pClassInit = NULL;也可以串行化!
    为什么还要要它们,起到什么效果?下面是我简化的可串行化的类:
    #pragma once
    // CSimple command targetclass CSimple : public CObject
    {
    public:
     //DECLARE_SERIAL( CSimple )
    _DECLARE_DYNCREATE(CSimple)
    CSimple();
     virtual ~CSimple();
     CString m_name;
     WORD   m_number; void Serialize( CArchive& archive );
    };// Simple.cpp : implementation file
    #include "stdafx.h"
    #include "TestSDI.h"
    #include "Simple.h"CSimple::CSimple()
    {
    }CSimple::~CSimple()
    {
    }
    //IMPLEMENT_SERIAL( CSimple, CObject, 1 )
    CObject* PASCAL CSimple::CreateObject() \
    { return new CSimple; } \
    extern AFX_CLASSINIT _init_CSimple; \
     _IMPLEMENT_RUNTIMECLASS(CSimple, CObject, 1, \
     CSimple::CreateObject, NULL) \
     AFX_CLASSINIT _init_CSimple(RUNTIME_CLASS(CSimple)); \// CSimple member functions
    void CSimple::Serialize( CArchive& archive )
    {
     // call base class function first
     // base class is CObject in this case
     CObject::Serialize( archive ); // now do the stuff for our specific class
     if( archive.IsStoring() )
      {
       m_name = "fasdfads";   archive << m_name << m_number;
      }
     else
      archive >> m_name >> m_number; TRACE2("%s %d\n",m_name ,m_number );
    }
      

  8.   

    [Quote=引用 15 楼 arong1234 的回复:]
    用不用没有关系,关键是理解内部,它的原理是怎么样,才能更好的用!否则就不需要提问 了哈
      

  9.   

    看看人家的注释         // Object I/O is pointer based to avoid added construction overhead.
    // Use the Serialize member function directly for embedded objects.
    friend CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb); friend CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb);
    friend CArchive& AFXAPI operator>>(CArchive& ar, const CObject*& pOb);
    这样做的目的其实很明确,加入我在一个文件中串行化很多对象,而对象的类型我并不知道,这时候我就可以用CObject的指针来做第二个参数,不需要预先了解下一个我要串行化的对象到底是什么类型的。如果你没有这种功能,怎么确保它成功串行化一个不知道啥类型的对象?
      

  10.   

    不要动不动就把它上升到“原理”这么高的级别,你知道那里有个友元支持ar >> pObj,下次你需要它时不就知道了么?
      

  11.   

    要是我遇到这种问题又不懂,我会全局搜索找到所有CArchive对象的使用地方,看看到底在哪用了。你提问的方式有问题,看你问题,似乎就是在说我们不应该提供友元,而实际上你要问的是为什么需要友元要知道,很多接口提供出来确实从来不用的,所以这里不一定有道理在里面,也许只是设计者担心未来需要所以加了。学习开始需要有种权衡的能力,如果一个不怎么经常用的细节不清楚,不妨放一放,你耽误很多天在这上面,实在很浪费,当你水平到一定程度,也许自然就懂了