Serialización de VC
La serialización es un mecanismo proporcionado por Microsoft para la E/S de archivos en objetos. Este mecanismo se ha utilizado ampliamente en el modo Marco/Documento/Ver. Mucha gente no tiene claro qué es la serialización, cómo hacer que los objetos sean serializables y cómo utilizar la función de serialización. Este artículo intenta dar una explicación sencilla de la serialización. Como no uso mucho la función de serialización, pido disculpas por cualquier defecto.
Lectura y escritura de archivos en el marco/documento/estructura de vista MFC
CFile es la clase base para todas las clases de archivos en la biblioteca de clases MFC. Todas las funciones de E/S de archivos proporcionadas por MFC están relacionadas con esta clase. En muchos casos, a todos les gusta llamar directamente a CFile::Write/WriteHuge para escribir archivos y llamar a CFile::Read/ReadHuge para leer archivos. Este tipo de E/S de archivos en realidad no es diferente de la E/S de archivos que no usa MFC, y ni siquiera es muy diferente de la E/S de archivos anterior de ANSI C. La única diferencia es que la API llamada es diferente. .
Cuando empieces a aprender C, debes estar muy familiarizado con cin/cout. Estos dos objetos utilizan los operadores muy claros lt;lt; y gt;gt; para realizar E/S. es:
//Código de muestra 1
int i;
cin gt; algo que objetar i
cout lt; i;
La ventaja de usar este método para E/S es que al usar la función de sobrecarga del operador, puede usar una declaración para completar. una serie de Lectura y escritura de objetos sin distinguir el tipo específico del objeto. MFC proporciona la clase CArchive, que implementa la sobrecarga de los operadores lt; lt; y gt;, con la esperanza de realizar E/S de archivos de la misma manera que cin y cout. Al cooperar con la clase CFile, no solo realiza la lectura y escritura de archivos de tipos simples como int/float, sino que también realiza la lectura y escritura de archivos de objetos serializables (Objetos serializables, este concepto se describirá más adelante).
Generalmente, el proceso de usar CArchive para leer objetos es el siguiente:
//Código de muestra 2
//Definir objetos de archivo y objetos de excepción de archivo
CFile file;
CFileException fe;
//Abre el archivo en modo lectura
if(!file.Open(filename, CFile: :modeRead,&fe))
{
fe.ReportError();
return;
} p>
//Crear objeto CArchive
CArchive ar(amp; archivo, CArchive::load);
ar gt; obj2gt; ..gt; gt; objn;
ar.Flush();
//Después de leer, cierra la secuencia de archivos
ar.Close();
file.Close();
El proceso de usar CArchive para escribir objetos es el siguiente:
//Código de muestra 3
//Definir objetos de archivo y objetos de excepción de archivo
CFile file;
CFileException fe;
//Abrir el archivo en modo lectura
if(!file.Open(nombre de archivo, CFile::modeWrite|CFile::modeCreate, amp;fe))
{
fe.ReportError();
return;
}
//Construir objeto CArchive
CArchive ar(amp; archivo, CArchive::load
);ar lt; obj1lt; lt; obj2lt; obj3...lt;
ar.Flush(); , Cerrar la secuencia del archivo
ar.Close();
file.Close();
Visible, para un archivo, si la disposición de los objetos en el archivo El orden es fijo, por lo que la única diferencia formal entre la lectura y escritura de archivos son los operadores utilizados. En la estructura marco/documento/vista de MFC, la composición de los objetos internos de un documento a menudo es fija. En este caso, el diseño de los objetos en el archivo cuando se escriben en el archivo también es fijo. Por lo tanto, CDocument utiliza la función virtual Serilize proporcionada por su clase base CObject para realizar la lectura y escritura automática de documentos.
Cuando el usuario selecciona el menú de archivos/abrir archivo (ID_FILE_OPEN) en la interfaz, se llama automáticamente a la función OnFileOpen de la clase derivada de CWinApp. Crea (MDI)/reutiliza (SDI) marcos, documentos y. Vea el objeto y finalmente llame a CDocument::OnOpenDocument para leer el archivo. El flujo de procesamiento de CDocument::OnOpenDocument es el siguiente:
//Código de muestra 4
BOOL CDocument:: OnOpenDocument(LPCTSTR lpszPathName )
{
if (IsModified())
TRACE0("Advertencia: OnOpenDocument reemplaza un documento no guardado.\n");
CFileException fe;
CFile* pFile = GetFile(lpszPathName,
CFile::modeRead|CFile::shareDenyWrite, amp;fe);
if (pFile == NULL)
{
ReportSaveLoadException(lpszPathName, amp; fe,
FALSE, AFX_IDP_FAILED_TO_OPEN_DOC
<); p> return FALSE ;}
DeleteContents();
SetModifiedFlag(); // sucio durante la deserializacion
CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
loadArchive.m_pDocument = this;
loadArchive.m_bForceFlat = FALSE;
TRY
{
CWaitCursor espera;
if (pFile-gt; GetLength() != 0)
Serialize(loadArchive); / cargarme
loadArchive.Close();
ReleaseFile(pFile, FALSE
}
CATCH_ALL(e)
{
ReleaseFile(pFile, TRUE);
DeleteContents(); // no se pudo eliminar el contenido
TRY
{
ReportSaveLoadException(lpszPathName, e,
FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
}
END_TRY
DELETE_EXCEPTION(e);
devuelve FALSO;
END_CATCH_ALL
SetModifiedFlag(FALSE); // comienza con sin modificar
return TRUE
}
De manera similar, cuando Cuando el usuario selecciona el menú archivo/guardar archivo (ID_FILE_SAVE) o archivo/guardar como... (ID_FILE_SAVEAS), finalmente se llama a CDocument::OnSaveDocument a través de CWinApp::OnFileSave y CWinApp::OnFileSaveAs. Esta función se procesa de la siguiente manera:
//Código de muestra 5
BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
{
CFileException fe;
CFile* pFile = NULL;
pFile = GetFile(lpszPathName, CFile::modeCreate |
CFile::modeReadWrite | CFile::shareExclusive, amp;fe);
if (pFile == NULL)
{
ReportSaveLoadException(lpszPathName, amp;fe,
TRUE, AFX_IDP_INVALID_FILENAME); p>
return FALSE;
}
CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
saveArchive.m_pDocument = esto;
saveArchive.m_bForceFlat = FALSE;
TRY
{
CWaitCursor espera;
> Serialize(saveArchive); // sálvame
saveArchive.Close();
ReleaseFile(pFile, FALSE); p> CATCH_ALL(e)
{
ReleaseFile(pFile, TRUE);
TRY
{
ReportSaveLoadException(lpszPathName, e,
TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
}
END_TRY
DELETE_EXCEPTION(e); >
devolver FALSO;
}
END_CATCH_ALL
SetModifiedFlag(FALSE); // volver a sin modificar
devolver VERDADERO; // éxito
}
Como se puede ver en los dos fragmentos de código anteriores, las estructuras de lectura y escritura de archivos son básicamente las mismas y, en última instancia, CObject::Serialize Se llama a la función para completar la propia lectura y escritura del documento (ver guardarme y cargarme en los comentarios). Para MDI y SDI generados automáticamente por AppWizard, el sistema genera automáticamente una implementación sobrecargada de esta función. La implementación predeterminada es:
//Código de muestra 6
void CMyDoc:: Serialize(. CArchiveamp; ar)
{
if (ar.IsStoring())
{
// TODO: agregue el código de almacenamiento aquí
}
else
{
// TODO: agregue el código de carga aquí
}
}
Si a una persona que está muy familiarizada con VC le gusta generar todo el código manualmente (por supuesto, esto es una pérdida de tiempo e innecesario), entonces la clase derivada CDocument que proporciona también This Se debe implementar la función Serialize predeterminada. De lo contrario, el sistema solo puede llamar a CObject::Serialize cuando lee y escribe archivos. Esta función no hace nada y, por supuesto, no puede completar el proceso de guardar/cargar archivos. Por supuesto, los usuarios también pueden interceptar menús como ID_FILE_OPEN para implementar sus propias funciones de lectura y escritura de archivos, pero dicho código se volverá muy engorroso y no fácil de leer.
Volver a CMyDoc::Función serializar.
Esta función determina si el archivo se está leyendo o escribiendo actualmente juzgando el objeto ar. Dado que AppWizard no sabe para qué sirve su documento, no agregará el código de lectura y escritura del archivo real por usted. Supongamos que hay tres objetos m_Obj_a, m_Obj_b, m_Obj_c en su documento, entonces el código real debería ser:
//Código de muestra 7
void CMyDoc::Serialize(CArchiveamp; ar)
{
if (ar.IsStoring())
{
ar lt; m_Obj_a lt; ;lt; m_Obj_c;
}
else
{
ar gt;gt; m_Obj_a gt;gt; ; m_Obj_c;
}
}
Objeto serializable (Objeto serializable)
Utilice el método en el código de muestra 7 Una condición básica para La E/S de archivos es que objetos como m_Obj_a deben ser objetos serializables. Las condiciones para un objeto serializable son:
Esta clase se deriva de CObject)
Esta clase implementa la función Serializar
Esta clase se define usando la macro DECLARE_SERIAL
La macro IMPLEMENT_SERIAL se utiliza en el archivo de implementación de la clase
Esta clase tiene un constructor sin parámetros, o un constructor con parámetros, todos los parámetros se proporcionan parámetros predeterminados
Aquí, la condición del objeto serializable no incluye tipos simples. Para tipos simples, CArchive básicamente implementa la sobrecarga de los operadores lt; y gt;, por lo que puede usar la serialización directamente para leer y escribir.
Derivado de la clase CObject
La serialización requiere que el objeto se derive de CObject o de una clase derivada de CObject. Este requisito es relativamente simple, porque casi todas las clases (excepto CString) se derivan de CObject, por lo que las clases que heredan de las clases MFC cumplen este requisito. Para su propia clase de datos, puede especificar su clase base como CObject para cumplir con este requisito.
Implementar la función Serializar
La función Serializar es la función que el objeto realmente guarda datos y es el núcleo de toda la serialización. El método de implementación es el mismo que CMyDoc::Serialize, usando CArchive::IsStoring y CArchive::IsLoading para determinar la operación actual y seleccionar lt;lt y gt;gt;
Utilice la macro DECLARE_SERIAL
La macro DECLARE_SERIAL incluye las funciones DECLARE_DYNAMIC y DECLARE_DYNCREATE. Define la información relacionada con CRuntimeClass de una clase e implementa la sobrecarga del operador gt; Después de implementar esta macro, CArchive puede usar ReadObject y WriteObject para realizar E/S de objetos y puede leer objetos de archivos sin conocer el tipo de antemano.
Utilice IMPLEMENT_SERIAL
La macro DECLARE_SERIAL y la macro IMPLEMENT_SERIAL deben aparecer en pares; de lo contrario, las entidades definidas por la macro DECLARE_SERIAL no se implementarán, lo que eventualmente provocará errores de conexión.
Constructor predeterminado
Este es el requisito de CRuntimeClass::CreateObject para objetos.
Casos especiales
Cuando el objeto se lee y escribe solo a través de la función Serialize sin usar ReadObject/WriteObject y la sobrecarga del operador, las condiciones de serialización anteriores no son requeridas, siempre que Serialize La función está implementada. Eso es todo.
Para las clases existentes, si no proporciona funcionalidad de serialización, se puede lograr usando el operador sobrecargado lt; y el operador gt;.
Ejemplo
Supongamos que necesita implementar un programa de edición y visualización de gráficos geométricos para admitir funciones de gráficos escalables. No quiero discutir aquí la implementación del sistema de gráficos específico, solo el guardado y carga de objetos de imagen.
Clase base CPicture
Cada objeto gráfico se deriva de CPicture. Esta clase implementa la función de serialización. Su código de implementación es:
//Imagen del archivo de encabezado. h
#if !definido(__PICTURE_H__)
#define __PICTURE_H__
#if _MSC_VER gt 1000
#pragma una vez p>
#endif // _MSC_VER gt; 1000
const int TYPE_UNKNOWN = -1;
clase CPicture: CObject público
{
int m_nType; //Categoría gráfica
DECLARE_SERIAL(CPicture)
public:
CPicture(int m_nType=TYPE_UNKNOWN): m_nType (m_nType) {};
int GetType()const {return m_nType;};
virtual void Draw(CDC * pDC);
void Serialize( CArchive amp; ar);
};
#endif
//archivo cpp imagen.cpp
#include "stdafx. h"
#incluir "imagen.h"
#ifdef _DEBUG
#definir nuevo DEBUG_NEW
#undef ESTE_FILE
carácter estático THIS_FILE[] = __FILE__;
#endif
void CPicture::Draw(CDC * pDC)
{
//La clase base no implementa la función de dibujo y es implementada por la clase derivada
}
void CPicture::Serialize(CArchive amp; ar)
{
if(ar.IsLoading())
{
ar lt; m_nType; /p >
ar gt; m_nTipo;
}
}
Nota: Dado que CRuntimeClass requiere que se cree una instancia de este objeto, aunque la función Draw no tiene ninguna operación de dibujo, esta clase no la define como una función virtual pura.
Proceso de guardado y E/S de archivos de objetos en clases derivadas de CDocument
Para simplificar el diseño, la clase de plantilla CPtrList proporcionada por MFC se utiliza para guardar objetos en CDocument- clases derivadas.
El objeto se define como:
protected:
CTypedPtrList m_listPictures;
Dado que ni CTypedPtrList ni CPtrList implementan la función Serialize, no puede pasar ar lt; m_listPictures y ar gt;gt; m_listPictures para serializar objetos, por lo que la función Serialize de CPictureDoc debe implementarse de la siguiente manera:
void CTsDoc::Serialize(CArchiveamp; ar)
{
POSITION pos;
if (ar.IsStoring())
{
// TODO: agregue el código de almacenamiento aquí p>
pos = m_listPictures.GetHeadPosition();
while(pos != NULL)
{
ar lt; (pos) ;
}
}
else
{
// TODO: agregar código de carga aquí p>
RemoveAll();
CPicture * pPicture
haz{
prueba
{ p>
ar gt; pPicture;
TRACE("Leer objeto d\n", pPicture-gt;
m_listPictures.AddTail(pPicture)
}
catch(CException * e)
{
e-gt;
romper;
}
}mientras(pImagen != NULL
}
m);
_pCurrent = NULL;
SetModifiedFlag(FALSE);
}
Implementar la función de serialización de clases derivadas
Programa de geometría que admite gráficos como ya que las líneas rectas, rectángulos, triángulos y elipses se implementan con las clases CLine, CRectangle, CTriangle y CEllipse respectivamente. Tome la clase CLine como ejemplo para implementar la función de serialización:
CLine derivada de CPicture y agregue las siguientes variables miembro a la definición de clase CLine: CPoint m_ptStart, m_ptEnd
Bajo esto; línea Agregue la siguiente macro a una línea: DECLARE_SERIAL(CLine)
Implemente la función Serializar void CLine::Serialize(CArchive amp; ar)
{
CPicture::Serialize( ar);
if(ar.IsLoading())
{
argt;gt;m_ptStart.xgt;gt;m_ptStart. ygt;gt;m_ptEnd .xgt;gt;m_ptEnd.y;
}else{
arlt;lt;m_ptStart.xlt;lt;m_ptStart.ylt;lt;m_ptEnd.xlt ;lt;m_ptEnd. y;
}
}
Agregar IMPLEMENT_SERIAL(CLine, CPicture, TYPE_LINE) en el archivo CPP; >La CLine definida de esta manera tiene función de serialización y otras clases de gráficos se pueden definir de manera similar.