MFC六大机制(六)序列化机制
一、对象的序列化
1、概念
序列化对象 – 将对象的类信息和对象的成员变量依次写入到文件的过程。
反序列化对象 – 从文件中读取类的信息,创建对象,并依次读取成员变量的值赋值给对象。
2、定义支持序列化的类
2.1 派生自CObject类
2.2 添加序列化的声明宏DECLARE_SERIAL(CStudent)
和实现宏IMPLEMENT_SERIAL(CStudent, CObject, 1)
2.3 重写CObject::Serialize()函数,在函数中,完成类的成员变量的序列化。
3、一个类的完整的序列化,要求父类,成员变量,都要支持序列化。
二、序列化小例子
1 #include "stdafx.h" 2 #include "Serialize2.h" 3 4 #ifdef _DEBUG 5 #define new DEBUG_NEW 6 #undef THIS_FILE 7 static char THIS_FILE[] = __FILE__; 8 #endif 9 10 ///////////////////////////////////////////////////////////////////////////// 11 // The one and only application object 12 13 CWinApp theApp; 14 15 using namespace std; 16 17 class CStudent : public CObject 18 { 19 //序列化的声明宏 20 //DECLARE_SERIAL(CStudent) 21 //_DECLARE_DYNCREATE(CStudent) 22 //_DECLARE_DYNAMIC(CStudent) 23 //声明宏的展开 24 //begin 25 public: 26 static AFX_DATA CRuntimeClass classCStudent; 27 28 virtual CRuntimeClass* GetRuntimeClass() const; 29 30 static CObject* PASCAL CreateObject(); 31 32 AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CStudent* &pOb); 33 //end 34 public: 35 CStudent(){} 36 CStudent(CString strName, int nAge) 37 { 38 m_strName = strName; 39 m_nAge = nAge; 40 } 41 //重写序列化成员虚函数 42 virtual void Serialize( CArchive& ar ); 43 44 void Show(); 45 46 public: 47 CString m_strName; 48 int m_nAge; 49 50 }; 51 //序列化的实现宏 52 //IMPLEMENT_SERIAL(CStudent, CObject, 1) 53 54 //实现宏的展开 55 //begin 56 //创建本类的对象返回指向该对象的指针 57 CObject* PASCAL CStudent::CreateObject() 58 { 59 return new CStudent; 60 } 61 62 //_IMPLEMENT_RUNTIMECLASS(CStudent, CObject, 1, CStudent::CreateObject) 63 64 //静态变量(运行时类信息)初始化 65 AFX_DATADEF CRuntimeClass CStudent::classCStudent = 66 { 67 "CStudent", 68 sizeof(class CStudent), 69 1, 70 CStudent::CreateObject, 71 RUNTIME_CLASS(CObject), 72 NULL 73 }; 74 75 //获取本类的静态变量地址(运行时类型信息地址) 76 CRuntimeClass* CStudent::GetRuntimeClass() const 77 { 78 /*return RUNTIME_CLASS(CStudent);*/ 79 return ((CRuntimeClass*)(&CStudent::classCStudent)); 80 } 81 82 //结构体变量-->目的:pModuleState->m_classList.AddHead(pNewClass); 83 //注意:是一个全局变量,在进入main()之间,已经被构造 84 AFX_CLASSINIT _init_CStudent(RUNTIME_CLASS(CStudent)); 85 /*struct AFX_CLASSINIT 86 { 87 AFX_CLASSINIT(CRuntimeClass* pNewClass) 88 { 89 AfxClassInit(pNewClass); 90 { 91 AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 92 AfxLockGlobals(CRIT_RUNTIMECLASSLIST); 93 //将本类的运行时类信息地址保存到当前程序模块状态的一个链表中 94 pModuleState->m_classList.AddHead(pNewClass); 95 AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST); 96 } 97 } 98 };*/ 99 100 //全局函数 在类中声明为友元,目的:访问类的私有成员 101 CArchive& AFXAPI operator>>(CArchive& ar, CStudent* &pOb) 102 { 103 pOb = (CStudent*) ar.ReadObject(RUNTIME_CLASS(CStudent)); 104 return ar; 105 } 106 //end 107 108 void CStudent::Serialize( CArchive& ar ) 109 { 110 CObject::Serialize(ar); 111 if(ar.IsStoring()) 112 { 113 ar<<m_strName<<m_nAge; 114 } 115 else 116 { 117 ar>>m_strName>>m_nAge; 118 } 119 } 120 121 void CStudent::Show() 122 { 123 printf("学生姓名:%s\n", m_strName); 124 printf("学生年龄:%d\n", m_nAge); 125 } 126 127 //写对象(序列化) 128 void ObjStore(CString strFileName, CStudent *pStu) 129 { 130 CFile file; 131 file.Open(strFileName, CFile::modeCreate | CFile::modeWrite); 132 CArchive ar(&file, CArchive::store); 133 ar<<pStu; 134 ar.Close(); 135 file.Close(); 136 } 137 138 //读对象(反序列化) 139 void ObjLoad(CString strFileName) 140 { 141 CFile file; 142 file.Open(strFileName, CFile::modeRead); 143 CArchive ar(&file, CArchive::load); 144 CStudent *pStu = NULL; 145 ar>>pStu; 146 ar.Close(); 147 file.Close(); 148 if (pStu) 149 { 150 pStu->Show(); 151 } 152 } 153 154 155 void WriteFile(CString strFileName) 156 { 157 //创建CFile对象 158 CFile file; 159 //创建/打开文件 160 file.Open(strFileName, CFile::modeCreate | CFile::modeWrite); 161 //向文件写入内容 162 CArchive archive(&file, CArchive::store); 163 archive<<10<<123.6<<\'A\'; 164 archive.Close(); 165 file.Close(); 166 } 167 168 void ReadFile(CString strFileName) 169 { 170 //创建CFile对象 171 CFile file; 172 //打开文件 173 file.Open(strFileName, CFile::modeRead); 174 //把文件中的内容读出来 175 //使用档案类 176 CArchive archive(&file, CArchive::load); 177 int nNum; 178 double dNum; 179 char cNum; 180 archive>>nNum>>dNum>>cNum; 181 printf("%d, %lg, %c\n", nNum, dNum, cNum); 182 } 183 184 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) 185 { 186 /*CString strFileName = "C:\\test.txt"; 187 WriteFile(strFileName); 188 ReadFile(strFileName);*/ 189 CStudent stu("张飞", 20); 190 CString strFileName = "C:\\serial.txt"; 191 ObjStore(strFileName, &stu); 192 ObjLoad(strFileName); 193 return 0; 194 }
四、实现原理
1 //写入对象的过程 2 ObjStore(strFileName, &stu); 3 { 4 ar<<pStu; 5 { 6 ar.WriteObject(pOb); 7 { 8 //获取运行时类信息 9 CRuntimeClass* pClassRef = pOb->GetRuntimeClass(); 10 //将运行时类信息写入到文件 11 WriteClass(pClassRef); 12 { 13 pClassRef->Store(*this); 14 { 15 //将类的版本号、类的大小、类名依次写入文件 16 WORD nLen = (WORD)lstrlenA(m_lpszClassName); 17 ar << (WORD)m_wSchema << nLen; 18 ar.Write(m_lpszClassName, nLen*sizeof(char)); 19 } 20 } 21 ((CObject*)pOb)->Serialize(*this); 22 { 23 //将成员变量依次写入文件 24 ar<<m_strName<<m_nAge; 25 } 26 } 27 } 28 } 29 30 //读取对象的过程 31 ObjLoad(strFileName); 32 { 33 ar>>pStu; 34 { 35 pOb = (CStudent*) ar.ReadObject(RUNTIME_CLASS(CStudent)); 36 { 37 CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag); 38 { 39 pClassRef = CRuntimeClass::Load(*this, &nSchema) 40 { 41 //依次从文件中读取版本号、类的大小、类名 42 ar >> wTemp; *pwSchemaNum = wTemp; 43 ar >> nLen; 44 45 if (nLen >= _countof(szClassName) || 46 ar.Read(szClassName, nLen*sizeof(char)) != nLen*sizeof(char)) 47 { 48 return NULL; 49 } 50 szClassName[nLen] = \'\0\'; 51 52 //获取当前程序模块状态信息 53 AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); 54 //遍历当前程序模块状态信息中的m_classList链表, 55 //如果找到与szClassName一样的类名的运行时类信息,返回该运行时类信息 56 for (pClass = pModuleState->m_classList; pClass != NULL; 57 pClass = pClass->m_pNextClass) 58 { 59 if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0) 60 { 61 AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST); 62 return pClass; 63 } 64 } 65 } 66 } 67 //动态创建CStudent类的对象 68 pOb = pClassRef->CreateObject(); 69 { 70 pObject = (*m_pfnCreateObject)(); 71 { 72 return new CStudent; 73 } 74 } 75 //调用Serialize()函数,为新创建的对象赋值 76 pOb->Serialize(*this); 77 { 78 ar>>m_strName>>m_nAge; 79 } 80 } 81 } 82 }
结语:要想学会MFC,还是要自己经常去断点,看源码,这样才有所理解,有所悟。