I read some of the macro implementations of MFC very early, RUNTIME_CLASS, DECLARE_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, etc. I am tired of reading them.
Code Implementation
(Note: The following macros and their implementations are taken from MFC)
Define:
# Define DECLARE_DYNAMIC (class_name )"
Public :"
Static const AFX_DATA CRuntimeClass class # class_name ;"
Virtual CRuntimeClass * GetRuntimeClass () const ;"
E. g.
DECLARE_DYNAMIC (RenderView)
(Note: RenderView is a class inherited from CFormView in MFC)
Equals:
Public:
Static const AFX_DATA CRuntimeClass classRenderView;
Virtual CRuntimeClass * GetRuntimeClass () const;
That is, declare a static CRuntimeClass variable and a virtual function GetRuntimeClass ()
About CRuntimeClass, its 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
};
Struct, with 6 members:
M_lpszClassName Class Name
M_nObjectSize object size
M_wSchema schema
M_pfnCreateObject function pointer (Object creation method)
M_pBaseClass/m_pfnGetBaseClass refers to the pointer to the Base Class Object/gets the pointer to the base class object function (the key to Runtime)
M_pNextClass points to the next class Object
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 ();
That is, declare a static CRuntimeClass variable, a virtual function GetRuntimeClass (), and a static function CreateObject ()
About CObject, its 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
};
Include: GetRuntimeClass () method, static variable CRuntimeClass classCObject, static method _ GetBaseClass () (NULL, because no Base), IsKindOf () method, etc.
# Define RUNTIME_CLASS (class_name) (CRuntimeClass *) (& class_name: class ## class_name ))
E. g.
RUNTIME_CLASS (RenderView)
Equals:
(CRuntimeClass *) (& RenderView: classRenderView ))
Converts the classRenderView static variable to the (CRuntimeClass *) pointer.
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, 0 xFFFF, RenderView: CreateObject)
Equals:
AFX_COMDAT const AFX_DATADEF CRuntimeClass RenderView: classRenderView = {
# RenderView, sizeof (class RenderView), 0 xFFFF, RenderView: CreateObject,
(CRuntimeClass *) (& CFormView: classCFormView), NULL };
CRuntimeClass * RenderView: GetRuntimeClass () const
{Return (CRuntimeClass *) (& RenderView: classRenderView ));}
(## Is the connection text, # RenderView is the RenderView string)
The implement includes the static classRenderView variable and the GetRuntimeClass () virtual function.
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, 0 xFFFF ,"
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), 0 xFFFF, RenderView: CreateObject,
(CRuntimeClass *) (& CFormView: classCFormView), NULL };
CRuntimeClass * RenderView: GetRuntimeClass () const
{Return (CRuntimeClass *) (& RenderView: classRenderView ));}
The implement includes the static classRenderView variable and the GetRuntimeClass () virtual function and the CreateObject () function.
Purpose
In summary, the purpose of this macro is to nest a CRuntimeClass object in the target object (such as RenderView) to support query conversion similar to the Runtime type (to support RTTI of MFC ?).
What is the purpose of supporting this? One use is DYNAMIC_DOWNCAST, that is, the object pointer implemented in MFC is converted from top to bottom at the class level:
E. g.
...
PCFormView * pView =...
PRenderView * pRenderView = DYNAMIC_DOWNCAST (RenderView, pView)
...
The implementation is as follows:
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; // encoded ed to the top, no match
}
Implementation principle: RenderView inherits from CFormView, and the latter inherits from CObject. They are nested with static CRuntimeClass objects, so you can query a pointer to the CFormView object (pCFormView) is it actually a pointer to the RenderView object by comparing the CRuntimeClass object (or its BaseRuntimeClass (or BaseRuntimeClass of BaseRuntimeClass ...) object) is notYes (compare pointer value)Is the static CRuntimeClass object (IsDerivedFrom method) contained in the RenderView class so simple?
If the object is the same (that is, the pointer value is equal), the conversion is successful. Otherwise, the conversion fails.
(Key: the embedded CRuntimeClass is static and can be accessed through classes and called through non-static functions of objects. This is the key to implementation. because each class inherited from the CObject level has a unique CRuntimeClass object corresponding to it, it can become an identifier of the type. If the identifier is the same, the types are certainly the same, and this identifier can be accessed both through classes and through objects at runtime, so it is named CRuntimeClass .)