很早看了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.)
轉載于:https://www.cnblogs.com/soroman/archive/2009/02/28/1400413.html