아래 발췌내용에서 조금 잘못된 부분이 있다.
런타임클래스를 이용하여 객체를 생성하는 것을 동적 생성이라고 용어정의를 내리는 것이
혼동을 줄 수 있다... 보통 동적 생성이라 함은 메모리 할당 함수나 연산자를 이용하여
실행중에 생성하는 것을 의미한다. new연산자를 이용하여 생성하는 것이 그 대표적인 예라고 볼 수 있다.
그리고 원래 DECLARE_DYNCREATE - IMPLEMENT_DYNCREATE 설정되었다고 해서 new 연산자를
사용하지 못하는 것은 아니다. 단지 런타임클래스의 m_pfnCreateObject 함수포인터 멤버(이 포인터는 클래스의 CreateObject 멤버 함수를 가리킴)를 이용하여 객체를 생성할 수 있는 기능을 추가한 것일 뿐이다.
다만 권고사항으로 위 매크로 선언시 생성자에 public 속성을 주지 않음으로써
정적인 생성이나 외부에서 new 연산자를 통한 동적 생성을 막도록 한다고들 한다...;;;
개념에 따른 당연한 귀결이 아니라는 소리이다.
/////////////// 발 췌 ////////////////////////////////
RTCI는 RTTI와 개념이 비슷하기는 하다.
여러분들도 잘 알겠지만 원래 STL이나 RTTI와 같은 개념은 MFC가 먼저 시작한 것이다.
예를 들어 MFC의 CList는 C++/STL의 vector와 같은 형태로 변형되었다.
하지만 요즘 시대는 STL의 사용도 많아지고 있으니... STL을 먼저 배우고 MFC를 배워서일까~?!
MFC에서는 실행 시간에 클래스 정보에 어떻게 접근하는지 알아보자. 다음 예를 보자.
아래의 킨 코드와 설명은 어떻게 보면 IsKindOf 함수를 설명하고자 함이 아닐까 ㅋㅋㅋ
#include <afxwin.h> // 이주성씨 제공 MFC 헤더 파일...
class CBase : public CObject
{
DECLARE_DYNCREATE(CBase)
};
IMPLEMENT_DYNCREATE(CBase, CObject)
class CTest : public CBase
{
DECLARE_DYNCREATE(CTest)
};
IMPLEMENT_DYNCREATE(CTest, CBase)
void main( void )
{
CTest *pTest = new CTest;
if( pTest->IsKindOf( RUNTIME_CLASS(CBase) ) )
{
puts( "CBase 상속" );
}
if( pTest->IsKindOf( RUNTIME_CLASS(CObject) ) )
{
puts( "CObject를 상속" );
}
if( pTest->IsKindOf( RUNTIME_CLASS(CTest) ) )
{
puts( "CTest 클래스" );
}
}
MFC에서 RTCI의 사용을 위해서 3개의 매크로 쌍을 다음과 같이 제공한다.
DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE
DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC
DECLARE_SERIAL / IMPLEMENT_SERIAL
위 3쌍의 매크로는 각각 IsKindOf 함수를 사용할 수 있도록 해준다. IsKindOf 함수는 위의 예와 같이
현재 포인터 또는 객체가 (1)특정 클래스로부터 상속을 받았는지 또는 (2)나의 클래스인지를 판단할 수 있게 한다.
여기에 사용된 RUNTIME_CLASS는 매크로이며, CObject 클래스를 classCObject와 같은 이름으로 변경해준다.
자세한 것은 아래를 참조하면 된다.
그리고 위의 각 매크로는 다음과 같은 차이를 갖고 있다. 각 클래스는 사용되는 매크로에 따라 사용할 수 있는 기능의
차이가 나는데, IsKindOf 함수는 모든 매크로가 다 사용할 수 있음을 알 수 있다. 또한 DECLARE_DYNAMIC 매크로는
CreateObject 함수를 호출할 수 없다. 그러므로 MDI 환경에서 CMainFrame 같은 경우는 DECLARE_DYNAMIC
매크로가 사용되고 있음을 알 수 있고, CChildFrame은 DECLARE_DYNCREATE가 사용되고 있음을 볼 수 있다.
그것은 단순한 우연이 아니며, CMainFrame은 초기에 한 번 생성되며, CChildFrame은 새로운 창이 생성될 때 마다
생성되어야 하기 때문이다. 클래스의 생성은 CreateObject를 사용해서 MFC 코드 내부적으로 실행된다.
SDI는 CMainFrame, Document, View가 모두 DECLARE_DYNCREATE로 되어 있는 것도 유의해서 볼 사항이다.
DECLARE_DYNCREATE로 매크로가 설정되어 있다는 것은 모든 클래스가 동적으로 생성된다는 것을 뜻하는 것이기
때문이다. 마지막으로 대부분 잘 사용하지 않을 수도 있는 매크로가 있는데 DECLARE_SERIAL은 Serialize에 사용되는
것이다. 여러분이 Serialize를 사용하기 위한 클래스를 만들려면, 반드시 CObject를 상속 받은 후 DECLARE_SERIAL
매크로를 사용해야 한다.
사용된 매크로 |
CObject:: IsKindOf |
CRuntimeClass:: CreateObject |
CArchive::operator>> CArchive::operator<< |
Basic CObject functionality |
사용 못함 |
사용 못함 |
사용 못함 |
DECLARE_DYNAMIC |
예 |
사용 못함 |
사용 못함 |
DECLARE_DYNCREATE |
예 |
예 |
사용 못함 |
DECLARE_SERIAL |
예 |
예 |
예 |
다음은 각 매크로의 코드이다.
매크로 끝의 \ 는 행 계속 문자이고, #은 해당 이름을 문자열로 만드는 것이고, ##은 두 개의 이름을 붙이는 것이다.
예를 들어, #abc -> "abc"로 변환되며, class##CTest -> classCTest로 변환된다.
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject(); // 왜 DECLARE_DYNCREATE가 CreateObject를 지원하는지.
#define DECLARE_DYNAMIC(class_name) \ // DECLARE_DYNAMIC은 CreateObject가 없다.
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
CRuntimeClass* PASCAL class_name::_GetBaseClass() \
{ return RUNTIME_CLASS(base_class_name); } \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
&class_name::_GetBaseClass, NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
다음을 보고 위 클래스의 DECLARE_DYNCREATE를 변형시켜보면 아래와 같다.
#include <afxwin.h>
class CBase : public CObject
{
//DECLARE_DYNCREATE(CBase)
protected:
static CRuntimeClass* PASCAL _GetBaseClass();
public:
static const AFX_DATA CRuntimeClass classCBase;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject(); // 왜 DECLARE_DYNCREATE가 CreateObject를 지원하는지.
};
...
위의 변환에서 보면, DECLARE_DYNCREATE는 _GetBaseClass(), GetRuntimeClass(), CreateObject() 함수와
classCBase라는 정적 멤버 변수로 변환됨을 알 수 있다. 각각의 함수들이 무엇을 하는지는 차차 알아보기로 하고,
여기서는 CRuntimeClass를 탐구해 보자. 이 클래스가 바로 RTCI의 핵심이 되는 클래스(구조체)이다.
struct CRuntimeClass
{
// 속성들
LPCSTR m_lpszClassName; // 클래스의 문자열 이름
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL // MFC Shared DLL을 사용하는 경우
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// 구현
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
좀 어렵죠? ㅋㅋㅋ 코딩해보구.. 테스트해보구.. 그러다 보면 쉬어져요...///
다음은 디버깅을 통하여 객체의 내부를 확인해 본 것이다. CObject라는 최상위 클래스에서 가상 함수 테이블이
보이며, 그 아래로 classCBase, classCTest가 존재하고 있다.
다음은 IMPLEMENT_DYNCREATE 매크로를 적용시킨 것이다.
#include <afxwin.h>
class CBase : public CObject
{
//DECLARE_DYNCREATE(CBase)
protected:
static CRuntimeClass* PASCAL _GetBaseClass();
public:
static const AFX_DATA CRuntimeClass classCBase;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject();
};
//IMPLEMENT_DYNCREATE(CBase, CObject)
CObject* PASCAL CBase::CreateObject()
{
return new CBase;
}
CRuntimeClass* PASCAL CBase::_GetBaseClass()
{
return ( (CRuntimeClass*)(&CObject::classCObject) );
}
AFX_COMDAT const AFX_DATADEF CRuntimeClass CBase::classCBase =
{
"CBase",
sizeof(class CBase),
0xFFFF,
CBase::CreateObject,
&CBase::_GetBaseClass,
NULL
};
CRuntimeClass* CBase::GetRuntimeClass() const
{
return ( (CRuntimeClass*)(&CBase::classCBase) );
}
이젠 나머지 매크로에 대해서도 스스로 해 보기를 바란다...
휴~~~~~~~~~~~~~~~~~~~~~~~
'MFC' 카테고리의 다른 글
강력한 에디터 CRichEditView (0) | 2008.08.30 |
---|---|
◎ MFC Class 레퍼런스(한글번역) (0) | 2008.08.30 |
지킴이 기초 강좌 (1) | 2008.08.29 |
CCmdTarget Class MFC (1) | 2008.08.07 |
MFC 클래스의 최상위 클래스 CObject (2) | 2008.08.07 |
MFC 클래스 계층도 (1) | 2008.08.07 |
DECLARE_DYNCREATE와 IMPLEMENT_DYNCREATE (2) | 2008.07.30 |