TN002: Formatar de dados do objeto persistente

Esta anotação descreve as rotinas MFC que oferecem suporte a objetos persistentes do C++ e o formato dos dados objeto quando ele é armazenado em um arquivo. Isso se aplica apenas a classes com o DECLARE_SERIAL e IMPLEMENT_SERIAL macros.

O problema

A implementação do MFC para dados persistentes se baseia em um formato binário compacto para salvar os dados para vários objetos em uma única parte contígua de um arquivo. Este formato binário fornece a estrutura para como os dados são armazenados, mas é o Serialize membro função do objeto que fornece o real dados salvos pelo objeto.

O MFC resolve o problema estruturante, usando a classe CArchive. Um objeto de CArchive fornece um contexto de persistência que dura desde o tempo do arquivo é criado até que a função de membro CArchive:: fechar é chamada, explicitamente pelo programador ou implicitamente pelo destruidor quando do escopo que contém o CArchive é encerrado.

Esta anotação descreve a implementação dos membros CArchive ReadObject e WriteObject. ReadObject e WriteObject não são chamados diretamente, em vez disso, são usados pelos produtos de operadores de inserção e extração de tipo seguro específicos de classe gerados automaticamente pelo DECLARE_SERIAL e IMPLEMENT_SERIAL macros.

classe CMyObject: público CObject
{
 nbsp;  DECLARE_SERIAL(CMyObject)
};

IMPLEMENT_SERIAL (CMyObj, CObject, 1)

/ / exemplo de uso (ar é um CArchive &)
CMyObject * pObj;
CArchive & ar;
ar << pObj;        / / chama ar.WriteObject(pObj)
ar >> pObj;        / / chama ar.ReadObject(RUNTIME_CLASS(CObj))

Esta anotação descreve código localizado no arquivo de origem MFC ARCOBJ.CPP. A principal implementação de CArchive pode ser encontrada em ARCCORE.CPP.

Salvando objetos para o armazenamento (CArchive:: WriteObject)

A função de membro CArchive:: WriteObject grava dados de cabeçalho usados para reconstruir o objeto. Esses dados consistem em duas partes: o tipo do objeto e o estado do objeto. Essa função membro também é responsável por manter a identidade do objeto que está sendo escrito, para que apenas uma única cópia é salva, independentemente do número de ponteiros de objeto (incluindo ponteiros circulares).

Salvando (inserir) e restauração de objetos (extraível) depende de vários "constantes manifesta". Estes são valores que são armazenados em binário e fornecem informações importantes para o arquivo (Observe o prefixo "w" indica as quantidades de 16-bit):

Marca Descrição
wNullTag Usado para ponteiros de objeto NULL (0).
wNewClassTag Indica a descrição de classe que segue é novo para este arquivo context—(-1).
wOldClassTag Indica a classe do objeto sendo lido tem sido visto neste contexto (0 x 8000).

Ao armazenar objetos, o arquivo mantém um CMapPtrToPtr (o m_pStoreMap) que é um mapeamento de um objeto armazenado para um identificador persistente de 32 bits (PID). Um PID é atribuído a cada objeto exclusivo e cada nome de classe exclusiva que é salvo no contexto de arquivo morto. Esses PIDs são entregues em seqüência começando em 1. É importante notar que esses PIDs não têm significado fora do escopo do arquivo e, em particular, não são deve ser confundido com o número de registro ou outros itens de identidade.

Começando com o MFC versão 4.0, a classe CArchive foi estendida para oferecer suporte a arquivos muito grandes. Nas versões anteriores, um PID foi uma quantidade de 16 bits, limitando o arquivamento para 0x7FFE (32766) objetos. As IDPs são agora 32 bits, mas eles são escritos como 16 bits a menos que eles sejam maiores que 0x7FFE. PIDs grandes são gravados como 0x7FFF seguido o PID de 32 bits. Esta técnica mantém compatibilidade com versões anteriores do arquivo.

Quando é feita uma solicitação para salvar um objeto em um arquivo (geralmente através do operador de inserção global), uma Marcar é feita para um ponteiro NULL CObject ; Se o ponteiro for NULL, o wNullTag é inserido no fluxo de arquivo morto.

Se temos um ponteiro de objeto real que é capaz de ser serializado (a classe é uma classe DECLARE_SERIAL ), em seguida, verificamos o m_pStoreMap para ver se o objeto foi salvo já. Se ele tiver, podemos inserir o PID de 32 bits associado com esse objeto.

Se o objeto não tiver sido salvo antes, há duas possibilidades que temos de ter em conta: o objeto e o tipo exato (isto é, classe) do objeto são novos para este contexto de arquivo morto, ou o objeto é de um tipo exato já visto. Para determinar se o tipo foi visto que podemos consultar o m_pStoreMap para um objeto de CRuntimeClass que corresponde ao objeto de CRuntimeClass associado ao objeto que está a guardar. Se nós vimos esta classe antes, WriteObject insere uma marca que é o OR'ing bit a bit de wOldClassTag e este índice. Se o CRuntimeClass for novo para este contexto de arquivo morto, em seguida, WriteObject atribui um novo PID dessa classe e inseri-lo para o arquivamento, precedido pelo valor wNewClassTag.

O descritor para essa classe é inserido em um arquivo usando o função de membro CRuntimeClass Store. CRuntimeClass:: Store insere o número de esquema da classe (veja abaixo) e o nome de texto ASCII da classe. Observe que o uso do nome de texto ASCII não garante exclusividade de arquivamento entre aplicativos, assim, é aconselhável a tag seus arquivos de dados para evitar a corrupção. Após a inserção de informações de classe, o arquivo coloca o objeto na m_pStoreMap e chama a função de membro Serialize para inserir dados específicos de classe no arquivo. Colocando o objeto na m_pStoreMap antes de chamar Serialize impede Múltiplo cópias do objeto sejam salvas para o armazenamento.

Ao retornar para o chamador inicial (geralmente a raiz da rede de objetos), é importante Fechar o arquivo. Se outras operações de CFile estão indo para ser feito, a função de membro CArchive Flush deve ser chamada. Falha para fazer isso resultará em um arquivo corrompido.

&Notanbsp;  Essa implementação impõe um limite rígido de 0x3FFFFFFE índices por contexto de arquivo morto. Esse número representa o número máximo de objetos exclusivos e classes que podem ser salvos em um arquivo único, mas nota que em um único disco arquivo pode ter um número ilimitado de contextos de arquivamento.

Carregando objetos do armazenamento (CArchive:: ReadObject)

Carregando objetos (extraível) usa a função de membro CArchive:: ReadObject e é o inverso de WriteObject. Como com WriteObject, ReadObject não é chamado diretamente pelo código do usuário; código do usuário deve chamar o operador de extração de tipo seguro que chama ReadObject com o esperado CRuntimeClass. Isso garante a integridade do tipo da operação de extração.

Uma vez que a implementação de WriteObject atribuído PIDs crescentes, começando com 1 (0 predefinido do objeto NULL), a implementação de ReadObject pode usar uma matriz para manter o estado do contexto de arquivo morto. Quando um PID é lido do armazenamento, se o PID for maior que o Ligado superior atual de m_pLoadArray, em seguida, ReadObject sabe que segue um novo objeto (ou descrição de classe).

Números de esquema

O número de esquema, que é atribuído à classe quando IMPLEMENT_SERIAL a classe é encontrado, é a "versão" de implementação da classe. O esquema refere-se a implementação da classe, não o número de vezes que um determinado objeto foi tornado persistente (normalmente referido como a versão de objeto).

Se você prete&nde manter várias implementações diferentes da mesma classe ao longo do tempo, incrementar o esquema como você revisar a implementação de função de membro do objeto Serialize lhe permitirá gravar código que pode carregar objetos armazenados usando versões mais antigas do implementation.nbsp;

A função de membro CArchive:: ReadObject lançará um CArchiveException quando encontrar um número de esquema no armazenamento persistente que difere o número de esquema na descrição de classe na memória. Não é fácil de recuperar essa exceção.

Você pode usar VERSIONABLE_SCHEMA ou tinha com sua versão esquema para manter essa exceção está sendo lançada. Usando VERSIONABLE_SCHEMA, seu código pode tomar a ação apropriada em sua função Serialize verificando o valor de retorno do CArchive:: GetObjectSchema.

Chamada serializar diretamente

Existem muitos casos onde a sobrecarga de esquema de arquivo de objeto geral de WriteObject e ReadObject não é necessário ou desejado. Este é o caso comum de serializar os dados em um CDocument. Neste caso a função de membro Serialize de CDocument é chamado diretamente, não com o extracto ou inserir operadores. O conteúdo do documento, por sua vez pode usar o esquema de arquivo de objeto mais geral.

Chamar Serialize diretamente tem as seguintes vantagens e desvantagens:

Porque Serialize é chamado diretamente em seu documento, não é geralmente possível para subobjetos do documento para arquivar as referências ao seu documento-pai. Esses objetos devem ser fornecidos um ponteiro para seu DocumentoContêiner explicitamente ou você deve usar o CArchive::MapObject função para mapear o ponteiro de CDocument um PID antes desses Voltar ponteiros são arquivados.

Como mencionado acima, você deverá codificar as informações de versão e classe-se ao chamar Serialize diretamente, permitindo que você altere o formato mais tarde e ainda manter compatibilidade com versões anteriores com arquivos antigos. A CArchive::SerializeClassRef função pode ser chamada explicitamente antes de serialização diretamente de um objeto ou antes de chamar uma classe base.

Técnico anotações por número |nbsp; &Notas técnicas por categoria

Index