(转)MFC的一些宏的整理 (DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE)

很早看了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.)

时间: 2024-10-31 15:53:54

(转)MFC的一些宏的整理 (DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE)的相关文章

C++编译时函数名修饰约定规则(很具体),MFC提供的宏,extern "C"的作用

调用约定: __cdecl __fastcall与 __stdcall,三者都是调用约定(Calling convention),它决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法. 1.__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈, 2._cdecl是C和C++程序的缺省调用方式.每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大.函数采用

【转】MFC消息映射详解(整理转载)

消息:主要指由用户操作而向应用程序发出的信息,也包括操作系统内部产生的消息.例如,单击鼠标左按钮,windows将产WM_LBUTTONDOWN消息,而释放鼠标左按钮将产生WM_LBUTTONUP消息,按下键盘上的字母键,将产生WM_CHAR消息. 消息处理:在MFC中,每个专门的处理函数单独处理每个消息.消息处理函数通常是某一类的成员函数,编写消息处理函数是编写框架应用程序的主要任务.可以使用ClassWizard创建消息处事函数,然后从Classwizard直接跳到源文件消息处理函数,编写处

iOS 开发中规范的宏定义,基本常用的宏定义整理集锦。分享给大家!

1 #ifndef MacroDefinition_h 2 #define MacroDefinition_h 3 //AppDelegate 4 5 #define APPDELEGATE [(AppDelegate*)[UIApplication sharedApplication] delegate] 6 //----------------------系统设备相关---------------------------- 7 //获取设备屏幕尺寸 8 #define SCREEN_WIDT

MFC宏

DECLARE_DYNCREATE DECLARE_DYNCREATE( class_name ) 参数: class_name 类的实际名字(不用引号括起来). 说明: 使用DECLARE_DYNCREATE宏可以使每个CObject的派生类的对象具有运行时动态创建的能力.框架利用这种能力来动态创建对象,例如,当它在串行化过程中从磁盘读取对象的时候.文档.视和框架类必须支持动态创建,因为框架需要动态地创建它们. 在类的.H模块中加入DECLARE_DYNCREATE宏,然后在每个需要访问这个类

MFC总结

一MFC 的概念和作用 1 什么是MFC? 全称Microsoft Foundation Class Library 我们称之为 微软基础类库,封装了绝大部分WIN32 api 函数,还封装 了程序流程. 1.1 硬盘存在形式就是一个库(静态库/动态库) 1.2 原理上还是一个程序框架 2 为什么使用MFC? 基于框架编程,提供工作效率,减少开发周期,节约开发 成本. 二几个重要头文件 afx.h - MFC 绝大部分类的声明 afxwin.h - 包含了afx.h 和windows.h afx

MFC学习笔记2--C++的重要性质

封装(encapsulation):类及其成员:属性,方法. 继承(Inheritance):基类(base class),派生类(derived class) this指针 虚函数与多态(Polymorphism) Object slicing与虚函数 静态成员(变量与函数) 构造函数与析构函数 四种不同的对象生成方式(in stack,int heap,global,local static) 1.在堆栈中产生一个对象: void MyFunc() { CFoo foo; ... } 2.在

MFC常见问题解惑[转]

MFC类的分类 1 Root: CObject :  CObject2 Application Architecture Classes: CWinApp/CFrameWnd/... 3 Window, Dialog, and Control Classes:CWnd/CDialog/...4 Drawing and Printing Classes : CGdiObject/CPrintInfo/...5 Simple Data Type Classes :CString/CRect/...6

【转】C语言中DEFINE简介及多行宏定义

要写好C语言,漂亮的宏定义是非常重要的.宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等. 在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义.那么究竟是用函数好,还是宏定义好?这就要求我们对二者进行合理的取舍. 我们来看一个例子,比较两个数或者表达式大小,首先我们把它写成宏定义: #define MAX( a, b) ( (a) > (b) (a) : (b) ) 其次,把它用函数来实现: int max( int a, int b)

[转载]c语言宏定义

一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对 C语言的学习很不利. 1 #define命令剖析 1.1   #define的概念 #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为