Pasos para DECLARE_SERIAL
Es sorprendente cómo ar determina qué clase debe crearse en función del archivo (énfasis en el archivo, no en codificación), ¿no es así?
Hay varios pasos:
1. Dado que DECLARE_SERIAL sobrecarga el operador >>, se garantiza que llamará a la función >> de la clase CMessg.
2. >>La función en realidad llama a la función ReadObject(CRuntimeClass*) de ar
3. ReadObject primero lee la información de juicio de clase (tal vez una cadena, también puede ser un índice de clase). ), y luego obtenga el nombre de clase de la clase correspondiente
4./ Hay una lista de todas las RuntimeClasses en el estado del módulo del programa, por lo que necesita encontrar el soporte para RuntimeClass; mediante el programa correspondiente (en comparación con ClassName), se obtiene la RuntimeClass correspondiente;
5.RuntimeClass contiene un método para crear objetos, CreateObject, al que se puede llamar para crear el objeto correspondiente. Aquí, dado que CreateObject es en realidad un objeto New, similar a new CMessg, para admitir la serialización, se debe utilizar el constructor sin parámetros;
6. Después de crear el objeto, llame a Seralize(ar) para leer la información del objeto real.
7. Devuelve el puntero del objeto.
8. pMessg apuntará al objeto correspondiente. Seis tecnologías clave para la simulación MFC
Las macros DECLARE_SERIAL / IMPLEMENT_SERIAL deberían sobrecargar los operadores << y >>, y colocar la función Serialize discretamente en la declaración de clase
y, finalmente, un buen enfoque. Todavía es usar macros.
El requisito previo para que una clase pueda leer y escribir archivos es que tenga la capacidad de generarse dinámicamente, por lo que MFC diseñó dos macros
DECLARE_SERIAL e IMPLEMENT_SERIAL:
#define DECLARE_SERIAL (nombre_clase)\
DECLARE_DYNCREATE(nombre_clase)\
operador amigo de CArchive& AFXAPI>> (CArchive& ar, nombre_clase* &pOb);
# define IMPLEMENT_SERIAL( nombre_clase, nombre_clase_base, wSchema)\
CObject* PASCAL nombre_clase::CreateObject() \
{ devuelve nuevo nombre_clase }\
_IMPLEMENT_RUNTIMECLASS(nombre_clase, nombre_clase_base, wSchema, \
nombre_clase::CreateObject) \
Operador CArchive&.AFXAPI>>(CArchive& ar, nombre_clase* &pOb) \
{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \
return ar }
Para poder procesar algunos antes de procesar (lectura) o escritura) cada objeto Para obtener detalles, como determinar si es la primera vez
aparece, número de versión de grabación, nombre del archivo de grabación, etc., CRuntimeClass requiere dos funciones: carga y almacenamiento
struct CRuntimeClass
{
// Atributos
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // Número de esquema de clases cargadas
UINT m_wSchema; // Número de esquema de clase cargada
Objeto* (PASCAL* m_pfnCreateObject)( // NULL => Clase abstracta
CRuntimeClass* m_pBaseClass;
CObject * CreateObject();
void Store(CArchive& ar)
estático CRuntimeClass* PASCAL; Load(CArchive& ar, UINT* pwSchemaNum);
// Los objetos CRuntimeClass están vinculados entre sí en una lista simple
static CRuntimeClass* pFirstClass // Inicio de la lista de clases
CRuntimeClass* m_pNextClass; // Lista vinculada de clases registradas
};
Ya has visto la función de carga en la sección anterior. Para simplificar, eliminé sus parámetros y. los reemplazó con el nombre de la clase en la pantalla
, cuando en realidad debería leer el nombre de la clase del archivo. De hecho, debería leer los nombres de las categorías del archivo.
En cuanto a la función Store, escribe el nombre de la categoría en el archivo:
// Código de serialización de la clase Runtime
CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar, UINT* pwSchema) .
{
WORD nLen;
char szClassName[64];
CRuntimeClass* pClass;
ar >> (WORD&)(* pwSchemaNum) >> nLen;
if (nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)
Devuelve NULL;
szClassName[nLen] = ~\0~;
for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
{
if (lstrcmp(szClassName, pClass->m_lpszClassName) == 0)
return pClass;
}
return NULL; // no encontrado
}
void CRuntimeClass: Write(m_l lpszClassName, nLen*sizeof(char));
}
clase CScribDoc: CDocumento público
{
DECLARE_DYNCREATE(CScribDoc)
...
};
clase CStroke: CObject público
{
DECLARE_SERIAL(CStroke)
público:
void Serialize(CArchive&);.
...
};
clase CRectangle: objeto CO público
{
DECLARE_SERIAL(CRectangle)
público:
void Serialize(CArchive&);
Void Serialize(CArchive&);
... amp;);
...
};
clase CCircle: CObject público
{
DECLARE_SERIAL(CCircle)
p>público:
void Serialize(CArchive& amp;...
...
}; p>
Y haga lo siguiente en el archivo .CPP:
IMPLEMENT_DYNCREATE(CScribDoc, CDocument)
IMPLEMENT_SERIAL(CStroke, CObject, 2)
IMPLEMENT_SERIAL(CRectangle, CObject, 1)
IMPLEMENT_SERIAL(CCircle, CObject, 1)
¿Y luego qué? Analicemos las funciones de serialización de CStroke, CRectangle y CCircle.
Por supuesto, no sorprende que el código fuente MFC de CObList y CDWordArray sea así:
// En el archivo de encabezado
clase CDWordArray : CObject público
{
DECLARE_SERIAL(CDWordArray)
público:
void Serialize(CArchive&);
...
};
clase CObList: CObject público
{
DECLARE_SERIAL(CObList)
public:
void Serialize(CArchive&);...
...
};
// En el archivo de implementación
IMPLEMENT_SERIAL(CObList, CObject, 0)
IMPLEMENT_SERIAL(CDWordArray, CObject, 0)
Y CObject tiene una función virtual adicional Serializar:
clase CObject
{
público:
virtual void Serialize(CArchive& ar);
...
}