#define _MFC_ {{| [[TableOfContents]] |}} = Serialize = MFC의 document 는 간단한 클래스가 아니다. 이 클래스는 일반적으로 다양한 객체들을 포함한다. 프로그램을 짜면서 이런 document 를 파일로 저장해야한다. 단순히 기본형의 데이터를 저장하고 불러들이기는 쉽지만, 객체단위로 이를 행하는 것은 대단히 어려운 일이다. 이를 위해서 MFC는 직렬화(Serialization)이라는 기능을 제공한다. 이 기능을 통해서 데이터를 저장하고 다시 읽는데 들이는 노력을 최소화 할 수 있다. = 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) 클래스의 데이터 멤버들을 직렬화하는 역할 IsStoring() 을 통해서 저장인지 로드인지를 판별한다. 구현은 프로그래머에 의해서 이루어진다. = CArchive Class = MFC기반의 스트림을 제공하는 클래스이다. 객체들을 파일에 출력하거나, 그것을 입력 스트립으로서 복구하는 스티리밍에 대한 메커니즘을 제공한다. 클래스 안에는 CFile 객체가 있으며, CArchive는 실제로 이 클래스를 통해서 파일 입출력을 전담시킨다. CArchive는 객체 데이터를 구성하는 로직을 처리하는 일을 전담한다. 기본 데이터 형을 처기하기 위해서 <<, >> 연산자가 오버라이딩되어있다. (float, double, BYTE, int, LONG, WORD, DWORD, CObject*, CString, SIZE, CSize, POINT, CPoint, RECT, CRect, CTime, CTimeSpan 이 오버라이딩 되어있다.) = 클래스에 직렬화 기능 추가 = 참고) [MFC/CObject] DECLARE_SERIAL()매크로를 통해서 직렬화 기능의 추가가 가능하다. 내부적으로 new, delete 를 사용하는데, 매모리 릭을 추적하는 코드가 들어가므로 특별히 프로그래머가 신경써야 하는 부분은 없다. 매크로는 클래스의 이름을 인수로 받는다. 따라서 만약 CExample 에 직렬화 기능을 추가한다면 CExample 의 정의부의 내부 어느 곳에서나 {{{~cpp DECLARE_SERIAL{CExample) //매크로 이므로 ;를 붙여선 안된다. }}} 를 추가하면 된다. (Class Wizard 가 관리하는 부분에 삽입하는 것은 제외 ㅡ.ㅡ;;) 구현 파일에 넣어야할 IMPLEMENT_SERIAL()는 3가지의 인수를 취한다. 첫번재는 이름, 두번재는 바로 윗 단계의 기본 클래스, 프로그램에 대한 스키마 번호(scheme number), 혹은 버전을 나타내는 부호 없는 32비트 정수를 인자로 받는다. 3번째 인자는 다른 버전의 프로그램으로 읽었을 때 발생하는 문제를 막아준다. {{{~cpp IMPLEMENT_SERIAL(CExample, CObject, 1) }}} 이후에 클래스 정의를 수정한다면 스키마 번호를 바꾸어야 한다. (다른 버전의 파일을 읽으려 하면 예외를 발생시킨다. 만약 CExampleDerived 가 CExample의 자식 클래스인데 이 클래스를 직렬화 하기위해서는 모든 중간 단계의 클래스 수준에서 직렬화가 구현되어야 한다. = 동작 방식 = 직렬화는 CDocument 객체에서 Serialize() 이벤트가 발생하게 되면 내부에 지정된 모든 멤버 변수들에게 Serialize 메시지를 보내서 결국엔 기본형의 데이터를 <<. >>와 같은 기 지정된 오버라이딩 함수를 통해서 처리하는 방식으로 이루어져있다. = 실제의 구현은? = * 클래스가 CObject 의 자식 클래스 인지 확인하라. * DECLARE_SERIAL() 매크로를 추가하라. 만약 바로 상위의 클래스가 CObject 가 아니라면 바로 상위의 클래스에 추가하라) * Serialize() 함수를 클래스의 멤버 함수로 선언하라. * IMPLEMENT_SERIAL() 매크로를 클래스 구현을 포함하고 있는 파일에 추가하라. * 클래스에대한 Serialize()를 구현하라. = Thread = Introspection 기능이 있는 다른 언어들에서의 Serialize 하는 모습에 대해서는 반드시 관찰해볼 필요가 있음.~ --[1002] ---- [MFC]