很早看了MFC的一些宏的实现,什么RUNTIME_CLASS, DECLARE_DYNAMIC,
DECLARE_DYNCREATE,IMPLEMENT_DYNCREATE, etc,看了就烦,现在整理下,免的忘了.
代码实现
(注:以下宏及其实现取自MFC)
- DECLARE_DYNAMIC
Define:
#define
DECLARE_DYNAMIC(class_name) "
public: "
static const AFX_DATA CRuntimeClass
class##class_name; "
virtual
CRuntimeClass* GetRuntimeClass() const; "
E.g.
DECLARE_DYNAMIC(RenderView)
(注:RenderView是继承于MFC中CFormView的一个类)
Equals:
public:
static const AFX_DATA CRuntimeClass
classRenderView;
virtual
CRuntimeClass* GetRuntimeClass() const;
即declare了一个static的CRuntimeClass变量和一个虚拟函数GetRuntimeClass()
关于CRuntimeClass,其declaration:
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int
m_nObjectSize;
UINT m_wSchema;
// schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); //
NULL => abstract class
#ifdef _AFXDLL
CRuntimeClass* (PASCAL*
m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif
//
Operations
CObject*
CreateObject();
BOOL
IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// Implementation
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
};
结构体,6个成员:
m_lpszClassName
类名字
m_nObjectSize 对象大小
m_wSchema schema
m_pfnCreateObject 函数指针 (对象创建方法)
m_pBaseClass/m_pfnGetBaseClass 指向基类对象的指针/获取基类对象函数的指针
(Runtime的关键)
m_pNextClass 指向下一个此类对象
- DECLARE_DYNCREATE
Define:
// not serializable, but
dynamically constructable
#define
DECLARE_DYNCREATE(class_name) "
DECLARE_DYNAMIC(class_name) "
static CObject* PASCAL CreateObject();
E.g.
DECLARE_DYNCREATE(RenderView)
Equals:
public:
static const AFX_DATA CRuntimeClass classRenderView;
virtual CRuntimeClass* GetRuntimeClass()
const;
static CObject* PASCAL
CreateObject();
即declare了一个static的CRuntimeClass变量和一个虚拟函数GetRuntimeClass()和一个static的函数CreateObject()
关于CObject,其declaration:
#ifdef _AFXDLL
class
CObject
#else
class
AFX_NOVTABLE CObject
#endif
{
public:
// Object model (types, destruction,
allocation)
virtual
CRuntimeClass* GetRuntimeClass() const;
virtual ~CObject(); // virtual
destructors are necessary
// Diagnostic allocations
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void*
p);
void PASCAL operator
delete(void* p);
#if _MSC_VER >= 1200
void PASCAL operator delete(void* p, void*
pPlace);
#endif
#if
defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
// for file name/line number tracking
using DEBUG_NEW
void* PASCAL
operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#if
_MSC_VER >= 1200
void PASCAL
operator delete(void *p, LPCSTR lpszFileName, int nLine);
#endif
#endif
// Disable the copy constructor and
assignment by default so you will get
// compiler errors instead of
unexpected behaviour if you pass objects
// by value or assign
objects.
protected:
CObject();
private:
CObject(const CObject&
objectSrc);
// no implementation
void
operator=(const CObject& objectSrc); //
no implementation
//
Attributes
public:
BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass)
const;
// Overridables
virtual void Serialize(CArchive&
ar);
#if defined(_DEBUG) ||
defined(_AFXDLL)
// Diagnostic
Support
virtual void
AssertValid() const;
virtual
void Dump(CDumpContext& dc) const;
#endif
//
Implementation
public:
static const AFX_DATA CRuntimeClass
classCObject;
#ifdef _AFXDLL
static CRuntimeClass* PASCAL
_GetBaseClass();
#endif
};
包含:GetRuntimeClass()方法,static变量 CRuntimeClass
classCObject,static方法 _GetBaseClass() (为NULL,因为没有Base),IsKindOf()方法等.
- RUNTIME_CLASS
#define RUNTIME_CLASS(class_name)
((CRuntimeClass*)(&class_name::class##class_name))
E.g.
RUNTIME_CLASS(RenderView)
Equals:
((CRuntimeClass*)(&RenderView::classRenderView))
即将classRenderView static变量转换成((CRuntimeClass*)指针
- IMPLEMENT_RUNTIMECLASS
Define:
#define
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) "
AFX_COMDAT const AFX_DATADEF CRuntimeClass
class_name::class##class_name = { "
#class_name,
sizeof(class class_name), wSchema, pfnNew, "
RUNTIME_CLASS(base_class_name), NULL }; "
CRuntimeClass*
class_name::GetRuntimeClass() const "
{ return
RUNTIME_CLASS(class_name); } "
E.g.
IMPLEMENT_RUNTIMECLASS(RenderView, CFormView, 0xFFFF,
RenderView::CreateObject)
Equals:
AFX_COMDAT const
AFX_DATADEF CRuntimeClass RenderView::classRenderView = {
#RenderView, sizeof(class RenderView), 0xFFFF,
RenderView::CreateObject,
((CRuntimeClass*)(&CFormView::classCFormView)), NULL };
CRuntimeClass*
RenderView::GetRuntimeClass() const
{ return
((CRuntimeClass*)(&RenderView::classRenderView));
}
(##为连接文本, #RenderView为取RenderView字符串)
即implement了static
classRenderView变量和GetRuntimeClass()虚拟函数
- IMPLEMENT_DYNCREATE
Define:
#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)
E.g.
IMPLEMENT_DYNCREATE(RenderView, CFormView)
Equals:
CObject* PASCAL
RenderView::CreateObject()
{ return new RenderView; }
AFX_COMDAT const AFX_DATADEF CRuntimeClass
RenderView::classRenderView = {
#RenderView,
sizeof(class RenderView), 0xFFFF, RenderView::CreateObject,
((CRuntimeClass*)(&CFormView::classCFormView)), NULL };
CRuntimeClass*
RenderView::GetRuntimeClass() const
{ return
((CRuntimeClass*)(&RenderView::classRenderView)); }
即implement了static
classRenderView变量和GetRuntimeClass()虚拟函数和CreateObject()函数.
用途
综合来看,这套宏的目的是在目标对象(比如RenderView)里面嵌套了一个CRuntimeClass对象,用来支持类似Runtime类型的查询转换等(用以支持MFC的RTTI?).
支持这些,有什么用呢?一个用处是DYNAMIC_DOWNCAST,即MFC里实现的对象指针在类层次上的从上到下转换:
E.g.
...
pCFormView*
pView = ...
pRenderView* pRenderView =
DYNAMIC_DOWNCAST(RenderView, pView)
...
其实现如下:
CObject* AFX_CDECL
AfxDynamicDownCast(CRuntimeClass* pClass, CObject* pObject)
{
if (pObject != NULL &&
pObject->IsKindOf(pClass))
return pObject;
else
return NULL;
}
BOOL
CObject::IsKindOf(const CRuntimeClass* pClass) const
{
ASSERT(this
!= NULL);
// it better be in
valid memory, at least for CObject size
ASSERT(AfxIsValidAddress(this,
sizeof(CObject)));
// simple SI case
CRuntimeClass* pClassThis =
GetRuntimeClass();
return
pClassThis->IsDerivedFrom(pClass);
}
BOOL CRuntimeClass::IsDerivedFrom(const
CRuntimeClass* pBaseClass) const
{
ASSERT(this != NULL);
ASSERT(AfxIsValidAddress(this, sizeof(CRuntimeClass), FALSE));
ASSERT(pBaseClass != NULL);
ASSERT(AfxIsValidAddress(pBaseClass, sizeof(CRuntimeClass), FALSE));
// simple SI
case
const CRuntimeClass*
pClassThis = this;
while
(pClassThis != NULL)
{
if (pClassThis ==
pBaseClass)
return TRUE;
#ifdef
_AFXDLL
pClassThis = (*pClassThis->m_pfnGetBaseClass)();
#else
pClassThis = pClassThis->m_pBaseClass;
#endif
}
return
FALSE; // walked to the top, no
match
}
实现原理:RenderView继承自CFormView,后者又继承自CObject,它们本身又嵌套了static
CRuntimeClass对象,那么查询一个指向CFormView对象的指针(pCFormView)是不是实际上就是一个指向RenderView对象的指针的功能是通过比较pCFormView指向的对象中的CRuntimeClass对象(或者其BaseRuntimeClass(或BaseRuntimeClass的BaseRuntimeClass...)对象)是不是就是(比较指针值)RenderView类所含的static
CRuntimeClass对象(IsDerivedFrom方法)这么简单了?
如果对象一样(即指针值相等)的话则可以转换成功,否则失败.
(关键:嵌入的CRuntimeClass是静态的,可以通过类访问,又可以通过对象的非静态函数调用,这是实现的关键.因为继承于CObject层次上的每个类都有唯一的CRuntimeClass对象与之对应,
所以它可以成为类型的一个标识符,如果表示符一样了,那么肯定类型是一样的,而这个标识符既可以通过类访问又可以在运行时刻通过对象访问,所以取名CRuntimeClass.)