none
关于MFC中IMPLEMENT_SERIAL的问题 RRS feed

  • 问题

  • 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
    {


    红林
    2009年2月4日 12:22

答案

全部回复

  •  
    问题2:
    struct CRuntimeClass
    {
     ........
     const AFX_CLASSINIT* m_pClassInit;//有什么用处?
    };
    红林
    2009年2月4日 12:24
  • 你不必使用每一个串行化的功能。
    CRuntimeClass用m_pClassInit和m_pfnCreateObject来支持动态创建。

    MSMVP VC++
    2009年2月4日 16:37
    版主
  •  

    问题:不用AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb), m_pClassInit = NULL;也可以串行化?
    下面是我简化的可串行化的类:
    #pragma once

    // CSimple command target

    class 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 );
    }



    红林
    2009年2月7日 2:17
  • 直接调用Serialize也是可以的
    MSMVP VC++
    2009年2月7日 2:30
    版主
  •  CRuntimeClass中m_pClassInit 有什么作用?
    红林
    2009年2月9日 12:42
  • 上面已经说过了。
    MSMVP VC++
    2009年4月9日 14:53
    版主