1. Serialize ¶
MFC의 document 는 간단한 클래스가 아니다. 이 클래스는 일반적으로 다양한 객체들을 포함한다.
프로그램을 짜면서 이런 document 를 파일로 저장해야한다. 단순히 기본형의 데이터를 저장하고 불러들이기는 쉽지만, 객체단위로 이를 행하는 것은 대단히 어려운 일이다.
이를 위해서 MFC는 직렬화(Serialization)이라는 기능을 제공한다. 이 기능을 통해서 데이터를 저장하고 다시 읽는데 들이는 노력을 최소화 할 수 있다.
프로그램을 짜면서 이런 document 를 파일로 저장해야한다. 단순히 기본형의 데이터를 저장하고 불러들이기는 쉽지만, 객체단위로 이를 행하는 것은 대단히 어려운 일이다.
이를 위해서 MFC는 직렬화(Serialization)이라는 기능을 제공한다. 이 기능을 통해서 데이터를 저장하고 다시 읽는데 들이는 노력을 최소화 할 수 있다.
2. inner Document Class ¶
.h
~cpp class CXXXDoc : public CDocument { protected: // create from serialization only CPainterDoc(); DECLARE_DYNCREATE(CPainterDoc) // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CPainterDoc) public: virtual BOOL OnNewDocument(); virtual void Serialize(CArchive& ar); //}}AFX_VIRTUAL }.cpp
~cpp IMPLEMENT_DYNCREATE(CXXXDoc, CDocument) void CXXXDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } }DECLARE_DYNCREATE
CXXXDoc 클래스의 객체가 시리얼화 입력과정동안 응용 프로그램을 통해 동적으로 생성될 수 있도록 한다. IMPLEMENT_DYNCREATE와 매치. 이 매크로는 CObject 에서 파생된 클래스에만 적용된다. 따라서 직렬화를 하려면 클래스는 직접적이든 간접적이든 CObject의 Derived Class 여야한다.
void Serialize(CArchive& ar)
직렬화를 하려는 모든 객체에는 이 메소드를 구현해주어야 한다. (has-a 관계에 있는 것도)
기본 생성자
직렬화 기능이 작동하는데 있어서 필요한 것. 이는 파일로 부터 객체를 읽을때 객체를 합성하는 프레임웍에 의해 사용된다.
IMPLEMENT_DYNCREATE
기본 클래스로부터 상속된 멤버들을 포함하여 CXXXDoc객체가 적절하게 동적으로 생성될 수 있도록 하는데 필요한 것이다.
void CXXXDoc::Serialize(CArchive& ar)
3. CArchive Class ¶
MFC기반의 스트림을 제공하는 클래스이다. 객체들을 파일에 출력하거나, 그것을 입력 스트립으로서 복구하는 스티리밍에 대한 메커니즘을 제공한다.
클래스 안에는 CFile 객체가 있으며, CArchive는 실제로 이 클래스를 통해서 파일 입출력을 전담시킨다.
CArchive는 객체 데이터를 구성하는 로직을 처리하는 일을 전담한다.
기본 데이터 형을 처기하기 위해서 <<, >> 연산자가 오버라이딩되어있다.
(float, double, BYTE, int, LONG, WORD, DWORD, CObject*, CString, SIZE, CSize, POINT, CPoint, RECT, CRect, CTime, CTimeSpan 이 오버라이딩 되어있다.)
클래스 안에는 CFile 객체가 있으며, CArchive는 실제로 이 클래스를 통해서 파일 입출력을 전담시킨다.
CArchive는 객체 데이터를 구성하는 로직을 처리하는 일을 전담한다.
기본 데이터 형을 처기하기 위해서 <<, >> 연산자가 오버라이딩되어있다.
(float, double, BYTE, int, LONG, WORD, DWORD, CObject*, CString, SIZE, CSize, POINT, CPoint, RECT, CRect, CTime, CTimeSpan 이 오버라이딩 되어있다.)
4. 클래스에 직렬화 기능 추가 ¶
참고) MFC/CObject
DECLARE_SERIAL()매크로를 통해서 직렬화 기능의 추가가 가능하다. 내부적으로 new, delete 를 사용하는데, 매모리 릭을 추적하는 코드가 들어가므로 특별히 프로그래머가 신경써야 하는 부분은 없다.
매크로는 클래스의 이름을 인수로 받는다. 따라서 만약 CExample 에 직렬화 기능을 추가한다면 CExample 의 정의부의 내부 어느 곳에서나
구현 파일에 넣어야할 IMPLEMENT_SERIAL()는 3가지의 인수를 취한다. 첫번재는 이름, 두번재는 바로 윗 단계의 기본 클래스, 프로그램에 대한 스키마 번호(scheme number), 혹은 버전을 나타내는 부호 없는 32비트 정수를 인자로 받는다.
3번째 인자는 다른 버전의 프로그램으로 읽었을 때 발생하는 문제를 막아준다.
만약 CExampleDerived 가 CExample의 자식 클래스인데 이 클래스를 직렬화 하기위해서는 모든 중간 단계의 클래스 수준에서 직렬화가 구현되어야 한다.
DECLARE_SERIAL()매크로를 통해서 직렬화 기능의 추가가 가능하다. 내부적으로 new, delete 를 사용하는데, 매모리 릭을 추적하는 코드가 들어가므로 특별히 프로그래머가 신경써야 하는 부분은 없다.
~cpp DECLARE_SERIAL{CExample) //매크로 이므로 ;를 붙여선 안된다.를 추가하면 된다. (Class Wizard 가 관리하는 부분에 삽입하는 것은 제외 ㅡ.ㅡ;;)
3번째 인자는 다른 버전의 프로그램으로 읽었을 때 발생하는 문제를 막아준다.
~cpp IMPLEMENT_SERIAL(CExample, CObject, 1)이후에 클래스 정의를 수정한다면 스키마 번호를 바꾸어야 한다. (다른 버전의 파일을 읽으려 하면 예외를 발생시킨다.
5. 동작 방식 ¶
직렬화는 CDocument 객체에서 Serialize() 이벤트가 발생하게 되면 내부에 지정된 모든 멤버 변수들에게 Serialize 메시지를 보내서 결국엔 기본형의 데이터를 <<. >>와 같은 기 지정된 오버라이딩 함수를 통해서 처리하는 방식으로 이루어져있다.