TN002: 영구 개체 데이터 형식

이 노트를 파일에 저장 하는 경우 영구 c + + 개체 및 개체 데이터 형식을 지 원하는 MFC 루틴을 설명 합니다. 이 DECLARE_SERIAL 및 IMPLEMENT_SERIAL 매크로와 클래스에만 적용 됩니다.

문제

영구 데이터에 대 한 MFC 구현 파일의 단일 연속 부분에 많은 개체에 대 한 데이터를 저장 하기 위한 컴팩트 이진 형식에 의존 합니다. 이 이진 형식은 데이터가 저장 되는 방법에 대 한 구조를 제공 하지만 개체에 저장 된 실제 데이터를 제공 하는 개체의 Serialize 멤버 함수。

MFC는 CArchive클래스를 사용 하 여 구조화 문제를 해결 합니다. CArchive 개체 제공 프로그래머에 의해 명시적으로 또는 CArchive 를 포함 하는 범위를 종료할 때 소멸자에 의해 암시적으로 CArchive::Close 멤버 함수를 호출할 때까지 보관 시간에서 지속 되는 지 속성에 대 한 컨텍스트가 만들어집니다.

이 노트 ReadObject 및 WriteObject CArchive 멤버의 구현을 설명합니다. ReadObject 및 WriteObject 으로 직접 호출 되지 않습니다 대신 DECLARE_SERIAL 및 IMPLEMENT_SERIAL 매크로 의해 자동으로 생성 된 클래스 특정 형식이 안전한 삽입 및 추출 연산자에 의해 사용 되는。

cMyObject 클래스: 공용 CObject
{
 nbsp;? DECLARE_SERIAL(CMyObject)
};

IMPLEMENT_SERIAL (CMyObj, CObject, 1)

/ / 사용 예 (아칸소는 CArchive 이다 &)
CMyObject * pObj;
CArchive & 아칸소;
아칸소 << pObj;??????? / / ar를 호출 합니다.WriteObject(pObj)
아칸소 >> pObj;??????? / / ar를 호출 합니다.ReadObject(RUNTIME_CLASS(CObj))

이 노트에서는 ARCOBJ MFC 소스 파일에 있는 코드를 설명 합니다.CPP입니다. 주요 CArchive 구현 ARCCORE에서 찾을 수 있습니다.CPP。

저장소 (CArchive::WriteObject)에 개체를 저장

멤버 함수 CArchive::WriteObject 개체를 재구성 하는 데 사용 하는 헤더 데이터를 씁니다. 이 데이터는 두 부분으로 구성 되어있습니다: 개체 유형과 개체의 상태. 이 멤버 함수는 또한 (를 포함 하 여 순환 포인터) 개체에 대 한 포인터의 수에 관계 없이 단일 복사본만 저장 되도록 작성 되는 개체의 id를 유지 하는 것에 대 한 책임。

(삽입) 저장 및 여러 "매니페스트 상수."에 의존 (압축) 개체 복원 이진 파일에 저장 되 고 보관 ("w" 접두사 16 비트 수량을 나타냅니다 참고)에 중요 한 정보를 제공 하는 값입니다.:

태그 설명
wNullTag NULL 개체 포인터 (0)에 대 한 사용。
wNewClassTag 아카이브 context?(-1)이 새로운 나오는 클래스 설명을 나타냅니다.
wOldClassTag 이 컨텍스트에서 (0x8000) 읽히고 개체의 클래스를 볼을 나타냅니다.

개체를 저장할 때 아카이브는 CMapPtrToPtr ( m_pStoreMap)는 저장 된 개체에서 32 비트 영구 식별자 (PID) 매핑을 유지 관리 합니다. PID는 모든 고유한 개체 및 보관의 맥락에 저장 된 모든 고유 클래스 이름에 할당 됩니다. 이러한 Pid는 1부터 시작 하는 순차적으로 밖으로 전달 됩니다. 이러한 Pid 의미가 없고 보관의 범위 밖에 서, 특히, 레코드 번호 또는 기타 신원 항목에 혼동 하지 않습니다 메모 하는 것이 중요。

MFC 버전 4.0 CArchive 클래스부터 매우 큰 아카이브를 지원 하도록 확장 되었습니다. 이전 버전에서 PID를 16-비트 수량 제한 0x7FFE (32766) 개체에 보관 했다. Pid 이제 32 비트, 하지만 그들은 작성 된 밖으로 16 비트 0x7FFE 초과 하지 않으면. 대형 Pid 0x7FFF 뒤에 32 비트 PID로 작성 됩니다. 이 기술은 파일 이전 버전과 호환성 유지。

NULL CObject 포인터;에 대 한 검사가 수행 됩니다 (일반적으로 글로벌 삽입 연산자)를 통해 보관 개체를 저장 하려면 요청이 있을 때 해당 wNullTag 아카이브 스트림에 삽입 포인터가 NULL 인 경우。

Serialize 할 수 있는 실제 개체 포인터를가지고 우리가 (클래스는 DECLARE_SERIAL 클래스), 우리는 개체가 이미 저장 되어 볼 수 m_pStoreMap 확인 합니다. 그것은 우리가 해당 개체와 관련 된 32 비트 PID 삽입。

개체를 전에 저장 하지 않은 경우에 우리가 고려해 야 하는 두 가지 가능성: 개체와 개체의 정확한 형식 (즉, 클래스)는 아카이브 컨텍스트에이 새로운 또는 이미 본 정확한 형식의 개체입니다. 경우 type 보아 왔다 우리 m_pStoreMap 우리가 저장 하는 개체와 관련 된 CRuntimeClass 개체와 일치 하는 CRuntimeClass 개체에 대 한 쿼리를 확인 합니다. 이 클래스 전에 살펴본 WriteObject Woldclasstag와이 인덱스의 비트 Or'ing이 태그를 삽입 합니다. 다음 WriteObject 해당 클래스에 새 PID를 할당 하 고이 보관, wNewClassTag 값 앞에 삽입할 CRuntimeClass 아카이브 컨텍스트가에 새로운 경우에,。

이 클래스에 대 한 설명자 저장소 CRuntimeClass 멤버 함수를 사용 하 여 아카이브를 다음 삽입 됩니다. CRuntimeClass::Store 클래스의 ASCII 텍스트 이름과 클래스 (아래 참조)의 스키마 번호를 삽입합니다. Note ASCII 텍스트 이름을 사용 하 여 응용 프로그램 간에 보관의 고유성을 보장 하지 않습니다, 따라서 그것은 태그 데이터 파일 손상을 방지 하는 것이 좋습니다. 클래스 정보를 삽입할 보관 m_pStoreMap 에 개체를 배치로 아카이브로 클래스 관련 데이터를 삽입 하려면 Serialize 멤버 함수를 호출 합니다. 저장소에 저장 되 고 개체의 여러 복사본을 방지 Serialize 를 호출 하기 전에 m_pStoreMap 개체 삽입。

초기 호출자 (일반적으로 루트 개체의 네트워크)를 반환 하는 경우 닫기 보관 하는 것이 중요 하다. 만약 다른 CFile 작업 해야 할 거 야, 플러시 CArchive 멤버 함수를 호출 해야 합니다. 그렇게 하지 않으면 손상 된 아카이브를 초래할 것 이다。

메모nbsp;??이 구현은 아카이브 컨텍스트 당 0x3FFFFFFE 인덱스의 하드 제한을 부과 합니다. 이 번호는 고유한 개체 및 단일 아카이브에 저장 될 수 있는 클래스의 최대 수를 나타냅니다 하지만 단일 디스크 파일 참고 아카이브 컨텍스트의 무제한 수를 가질 수 있습니다(&N).

저장소 (CArchive::ReadObject)에서 개체를 로드합니다.

(압축) 개체를 로드 CArchive::ReadObject 멤버 함수를 사용 하 고 WriteObject반대입니다. 로 WriteObject를 함께 ReadObject 호출 하지 않습니다 직접 사용자 코드; 사용자 코드는 ReadObject 예상된 CRuntimeClass와 호출 하는 형식이 안전한 추출 연산자를 호출 해야 합니다. 이 추출 작업의 형식 무결성을 보장 한다.

이후 WriteObject 구현 증가 Pid를 할당, 1부터 시작 (0은 NULL 개체 미리 정의), ReadObject 구현 아카이브 컨텍스트 상태를 유지 하도록 배열을 사용할 수 있습니다. PID는 읽을 때 저장소에서 PID m_pLoadArray의 현재 상한 보다 크면, 다음 ReadObject 알고 새 개체 (또는 클래스 설명) 다음。

스키마 번호

클래스 IMPLEMENT_SERIAL 발생 하는 경우 클래스에 할당 된 스키마 숫자 클래스 구현의 "버전"입니다. 클래스의 구현에 참조 하는 스키마, 횟수를 하지 주어진된 개체 만들어왔다 영구 (보통 개체 버전 추천)。

시간이 지남에 같은 클래스의 여러 가지 구현을 유지 하고자 하는 경우 개체의 Serialize 멤버 함수 구현이 수정으로 스키마를 증가 하면는 implementation.nbsp의 이전 버전을 사용 하 여 저장 된 개체를 로드할 수 있는 코드 작성(&N);

스키마 수가 메모리에 클래스 설명 다른 영구 저장소에서 스키마 번호를 발견 했을 때 CArchive::ReadObject 멤버 함수 CArchiveException 를 던 질 것 이다. 이 예외를 복구 하는 것이 쉽지 않다。

VERSIONABLE_SCHEMA 를 사용할 수 또는 던져 되 고에서이 예외를 유지 하려면 스키마 버전 했다. VERSIONABLE_SCHEMA를 사용 하 여 코드 걸릴 수 있습니다의 Serialize 함수에 적절 한 조치를 CArchive::GetObjectSchema 에서 반환 값을 검사 하 여。

전화를 직접 Serialize

ReadObject 및 WriteObject 일반 개체 보관 체계의 오버 하지 않는 필요 하거나 원하는 경우가 많이 있습니다. CDocument로 데이터를 serialize 하는 일반적인 경우입니다. 이 경우 CDocument 의 Serialize 멤버 함수를 직접 호출 하지 추출 된 또는 연산자를 삽입 합니다. 문서의 내용 보다 일반적인 개체 보관 체계를 사용 차례 수 있습니다.

Serialize 를 직접 호출 하는 다음과 같은 장점 및 단점:

귀하의 문서에 직접 Serialize 를 호출 하기 때문에 그것은 일반적으로 그들의 부모 문서에 대 한 참조를 보관 문서의 하위에 대 한 가능한. 이러한 개체 주어져야 포인터의 컨테이너 문서에 명시적으로 또는 CArchive::MapObject 함수를 사용 하 여 이러한 다시 포인터 보관 전에 CDocument 포인터를 PID를 매핑할 해야。

위에서 설명 했 듯이 해야 인코딩할 버전 및 클래스 정보를 스스로 오래 된 파일을 이전 버전과 호환성을 유지 하면서 나중에 형식을 변경할 수 있습니다 직접 Serialize를 호출할 때. 직접 개체를 serialize 또는 기본 클래스를 호출 하기 전에 CArchive::SerializeClassRef 함수를 명시적으로 호출할 수 있습니다.

기술 노트 번호로 |nbsp; 카테고리별 기술 노트(&N)

Index