U E D R , A S I H C RSS

MFC/Serialize

{{| |}}

1. Serialize

MFC의 document 는 간단한 클래스가 아니다. 이 클래스는 일반적으로 다양한 객체들을 포함한다.
프로그램을 짜면서 이런 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)
클래스의 데이터 멤버들을 직렬화하는 역할
IsStoring() 을 통해서 저장인지 로드인지를 판별한다.
구현은 프로그래머에 의해서 이루어진다.

3. CArchive Class

MFC기반의 스트림을 제공하는 클래스이다. 객체들을 파일에 출력하거나, 그것을 입력 스트립으로서 복구하는 스티리밍에 대한 메커니즘을 제공한다.
클래스 안에는 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 의 정의부의 내부 어느 곳에서나
~cpp  DECLARE_SERIAL{CExample)		//매크로 이므로 ;를 붙여선 안된다.
를 추가하면 된다. (Class Wizard 가 관리하는 부분에 삽입하는 것은 제외 ㅡ.ㅡ;;)

구현 파일에 넣어야할 IMPLEMENT_SERIAL()는 3가지의 인수를 취한다. 첫번재는 이름, 두번재는 바로 윗 단계의 기본 클래스, 프로그램에 대한 스키마 번호(scheme number), 혹은 버전을 나타내는 부호 없는 32비트 정수를 인자로 받는다.
3번째 인자는 다른 버전의 프로그램으로 읽었을 때 발생하는 문제를 막아준다.
~cpp IMPLEMENT_SERIAL(CExample, CObject, 1)
이후에 클래스 정의를 수정한다면 스키마 번호를 바꾸어야 한다. (다른 버전의 파일을 읽으려 하면 예외를 발생시킨다.

만약 CExampleDerivedCExample의 자식 클래스인데 이 클래스를 직렬화 하기위해서는 모든 중간 단계의 클래스 수준에서 직렬화가 구현되어야 한다.

5. 동작 방식

직렬화는 CDocument 객체에서 Serialize() 이벤트가 발생하게 되면 내부에 지정된 모든 멤버 변수들에게 Serialize 메시지를 보내서 결국엔 기본형의 데이터를 <<. >>와 같은 기 지정된 오버라이딩 함수를 통해서 처리하는 방식으로 이루어져있다.

6. 실제의 구현은?

  • 클래스가 CObject 의 자식 클래스 인지 확인하라.
  • DECLARE_SERIAL() 매크로를 추가하라. 만약 바로 상위의 클래스가 CObject 가 아니라면 바로 상위의 클래스에 추가하라)
  • Serialize() 함수를 클래스의 멤버 함수로 선언하라.
  • IMPLEMENT_SERIAL() 매크로를 클래스 구현을 포함하고 있는 파일에 추가하라.
  • 클래스에대한 Serialize()를 구현하라.

7. Thread

Introspection 기능이 있는 다른 언어들에서의 Serialize 하는 모습에 대해서는 반드시 관찰해볼 필요가 있음.~ --1002

----
MFC
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:42
Processing time 0.0220 sec