Serialization을 구현하기 위하여 우선 가장 기초가 되어야할 사항은 새로운 클래스를 반드시 CObject 클래스로부터 상속받아야 하는 점입니다. 하지만, 이것이 다는 아니며 약간의 작업이 더 필요하다는 것은 잘 아실 것입니다.
어떤 목적의 클래스인지에 따라서 적절한 매크로 함수를 사용해야 할 것입니다. 그래서, 세 가지 종류로 구분이 되어있습니다.
IMPLEMENT_DYNAMIC(), DECLARE_DYNAMIC(): CRuntimeClass를 통한 런타임 클래스 정보를 사용할 수 있도록 필요한 선언을 매크로 함수 한 줄로서 자동으로 추가할 수 있도록 만들어줍니다. DECLARE_DYNAMIC(현재 클래스 이름): 이것은 클래스의 선언부 (헤더 파일)에서 직접 써주시면 자동으로 관련된 내용을 클래스에 선언해줍니다. IMPLEMENT_DYNAMIC(현재 클래스 이름, 상위 클래스 이름): 이것은 클래스의 소스 코드 부분 (CPP 파일)의 상단부에 기재하여 주시면 됩니다.
IMPLEMENT_DYNCREATE(), DECLARE_DYNCREATE() : 런타임 정보와 동적 개체 생성, Assert/Valid 등을 지원합니다. 사용 방법은 역시 동일합니다.
IMPLEMENT_SERIAL(), DECLARE_SERIAL(): CObject 클래스에서 사용할 수 있는 모든것을 가능하도록 지원합니다. 동적 개체 생성, 직렬화 등등이 있겠군요. 사용 방법은 역시 동일합니다.
Windows 기반에서 웹 그래픽을 처리하기 위해서는 이전까지는 JPEG나 GIF 이미지의 Static Library 또는 관련 DLL들을 얻어서 써야 하는 매우 번거로운 작업을 해야만 했습니다. 하지만, MFC 7.0에서는 이러한 수고스러움을 덜어주기 위하여 새로운 클래스를 도입하게 되었는데, 바로 CImage 클래스입니다.
CImage 클래스는 CBitmap 클래스의 확장 개념입니다. 기존의 비트맵 이미지 객체로도 손쉽게 불러올 수 있음은 물론이고 기본적인 수준의 GIF, JPEG, PNG, BMP 이미지를 사용할 수 있으므로 공들여서 웹 그래픽 작업을 번거롭게 하는 일은 없어졌습니다.
여러분은 이미지를 불러들이기 위하여 두 가지 함수를 사용하실 수 있습니다. Load()와 LoadFromResource()라는 함수입니다. Load() 함수는 파일 경로명을 지정하거나 IStream 인터페이스의 포인터 객체를 얻는 방법이 있습니다. 대개의 경우는 파일 경로명을 사용하겠지요. LoadFromResource()는 리소스의 이름 데이터나 리소스 ID를 지정할 수 있습니다.
이렇게 불러들인 이미지는 기존의 CBitmap 클래스처럼 사용하시면 됩니다. 이미지를 출력하기 원하는 DC에 뿌려주기만 하면 됩니다. 비트맵 핸들을 얻어야할 필요가 있다면 연산자 오버로딩이 되어져 있기 때문에 그냥 HBITMAP에 대입해서 쓰면 됩니다.
Visual C++ .NET, 즉 MFC 7.0에서는 이전 버전인 MFC 6.2보다 편리하게 변경된 사항이 몇 가지가 더 있습니다. 그 중에 하나가 CObject 클래스에서의 직접 상속입니다. 물론 이전에도 아래와 같은 절차를 통하여 상속받는 것을 지원하였지만 .NET 버전에 들어와서부터는 좀 더 처리가 간결해졌다는 뜻입니다.
MFC 클래스를 작성할 때에 클래스 작성 마법사는 상속 가능한 클래스 리스트를 보여줍니다. 이 때에 CObject를 포함한 다수의 새로운 클래스를 제공합니다.
여러분은 여기서 CObject로 상속을 받아서 몇 가지의 작업을 더해주면 CObject와 동일한 기능을 수행하는 또 하나의 여러분만의 새로운 데이터 클래스를 생성할 수 있습니다.
이전에도 작성했었던 방법이지만 MFC 7.0에서도 그대로 사용할 수 있습니다. 아니, 약간 더 구체적으로 정보를 더 알려드리도록 하겠습니다. 클래스의 사용 용도에 따라서 여러분은 선언의 수준을 달리할 수 있습니다.
* DECLARE_DYNAMIC(class_name) 및 IMPLEMENT_DYNAMIC(class_name, base_class_name) : CRuntimeClass* 구조체로 조사가 가능한 일반적인 형태의 런타임 클래스로 만드는데에 필요한 약간의 데이터를 더 포함합니다. #DEFINE 선언이기 때문에, 프리프로세서가 컴파일 직전에 실제 코드로 변환하여 처리하도록 되어있습니다.
DECLARE_DYNAMIC() 선언은 클래스의 선언부에 직접 사용하시면 됩니다. 단 주의하실 점은 DECLARE_DYNAMIC() 선언 그 자체가 액세스 권한 적용 대상이 아니라는 점입니다. 즉, 아래와 같은 선언을 할 경우 컴파일 에러가 발생할 수 있습니다.
class CExample : public CObject
{
public:
DECLARE_DYNAMIC(CExample)
CExample();
virtual ~CExample();
};
위와 같은 코딩을 올바르게 고치는 방법은, DECLARE_DYNAMIC() 선언을 public: 영역 밖에서 코딩해주는 것입니다.
그리고, DECLARE_DYNAMIC() 선언으로 끝나면 안되며, DECLARE_DYNAMIC()과 연관되는 멤버 함수의 선언을 위하여 IMPLEMENT_DYNAMIC()을 소스 코드 파일 (.cpp)의 프리프로세서 선언문 바로 아래에 넣으셔야 합니다.
* DECLARE_DYNCREATE(class_name) 및 IMPLEMENT_DYNCREATE(class_name, base_class_name) : 이 매크로는 앞서 거론한 DECLARE_DYNAMIC()과 IMPLEMENT_DYNAMIC()의 내용을 포함함과 동시에, CDocTemplate와 같은 클래스에서의 동적 생성 프로세스를 지원하도록 해줍니다.
기본적인 사용 방법은 위와 같습니다. 하지만, 위의 선언을 이미 하였을 경우 그 선언 대신 사용해야 합니다. 그렇지 않으면 컴파일 시 중복 선언 에러를 발생하게 됩니다. 또한 최소한 1개 이상의 기본 생성자가 필요합니다.
* DECLARE_SERIAL(class_name) 및 IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) : 이 매크로는 앞서 거론한 DECLARE_DYNCREATE() 및 IMPLEMENT_DYNCREATE()의 내용을 포함함과 동시에, 직렬화 프로세싱을 지원하도록 해줍니다. 이 선언이 이루어져야 올바르게 Serialize(CArchive& ar) 함수를 재정의할 수 있습니다. wSchema에는 여러분이 임의의 버전을 입력하도록 되어있습니다. 대개 0에서부터 시작하며 프로그램의 메이저 버전을 올릴 때에 같이 올리곤 합니다.
역시 사용법은 같습니다. 중복 선언에 유의할 것과, 최소 1개 이상의 기본 생성자가 요구됨을 주의하면 됩니다.
다음 Chapter에서는 이렇게 선언한 매크로를 어떻게 사용할 수 있는지를 알아보겠습니다.
당신의 의견을 작성해 주세요.