MFC技术内幕系列之(三)----MFC执行期类型识别与动态创建技术内幕

2023-12-16 21:58

本文主要是介绍MFC技术内幕系列之(三)----MFC执行期类型识别与动态创建技术内幕,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


                     /********* 文章系列:MFC技术内幕系列***********/
                     /************MFC技术内幕系列之(三)***********/
                     /*文章题目:MFC执行期类型识别与动态创建技术内幕*/
                     /*                   Copyright(c)2002 bigwhite                    */
                     /*                          All rights Reserved                         */
                     /*******关键字:执行期类型识别,动态创建*******/
                     /*                          时间:2002.7.23                            */
                     /*    注释:本文所涉及的程序源代码均在Microsoft    */
                     /          Visual Studio.Net Enterprise Architect Edition      /
                     /*                  开发工具包提供的源代码中                  */
                    

引言:
    众所周知,微软的MFC Application Framework建立在一系列先进的程序设计技术上的。比如:消息映射机制,命令传递机制,执行期类型识别与动态创建技术及文档序列化技术等。其中执行期类型识别与动态创建技术是其中最重要的技术之一。微软在MFC中用一些神秘的宏实现了这种机制,但是对于学习MFC程序设计的初学者来说它却成为了一大难点,所以在这篇文章中我将详细地为大家挖掘其中的内幕。

正文:
    MFC执行期类型识别与动态创建技术是借助CRuntimeClass结构和一系列神秘的宏实现的。而动态创建技术 的前提是执行期类型识别网的建立。下面就让我们来看看执行期类型识别网是如何建立起来的?
                       ///
                       /*一. 1.CRuntimeClass结构总览 */
                       ///
   注释:CRuntimeClass结构定义在../Visual Studio.NET/vc7/atlmfc/include/Afx.h中
// object type information  
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;

 // dynamic name lookup and creation
 static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);//for ANSI
 static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);//for Unicode
 static CObject* PASCAL CreateObject(LPCSTR lpszClassName);  for ANSI
 static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);//for Unicode
// 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
 const AFX_CLASSINIT* m_pClassInit;
};

                       /
                       /* 一.2.执行期类型识别网组成部分 */
                       //
  在CRuntimeClass结构中与执行期类型识别网建立有关的成员如下:
  struct CRuntimeClass
  { 
      ...//
      LPCSTR m_lpszClassName;
      CRuntimeClass* m_pBaseClass;
            
   }

 
                       //
                       /*一.3.执行期类型识别网的连接建立*/
                       //
   MFC在每一个具有执行期类型识别能力的类的.h和.cpp文件中都添加了两个宏。他们是:
//in xx.h
  class class_name
  {
      DECLARE_DYNAMIC(class_name)
      ...//
  }
//in xx.cpp
  IMPLEMENT_DYNAMIC(class_name, base_class_name)
  ...//
  这两个宏展开后是什么样子呢?让我们看看其源代码吧!
  注释:这两个宏定义在../Visual Studio.NET/vc7/atlmfc/include/Afx.h中
 
  #define DECLARE_DYNAMIC(class_name) /
  public: /
 static const CRuntimeClass class##class_name; /
 virtual CRuntimeClass* GetRuntimeClass() const; /


  #define IMPLEMENT_DYNAMIC(class_name, base_class_name) /
 IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

 
  #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) /
  AFX_COMDAT const CRuntimeClass class_name::class##class_name = { /
  #class_name, sizeof(class class_name), wSchema, pfnNew, /
   RUNTIME_CLASS(base_class_name), NULL, class_init }; /
  CRuntimeClass* class_name::GetRuntimeClass() const /
  { return RUNTIME_CLASS(class_name); } /
  相关宏定义如下:
  #define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
  #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
 
  看清了吧!这两个宏互相配合着在每个类中都塞进了点东东,至于什么东东,自己看呗儿!
 
  下面我以一个具体的类来展示以下执行期类型识别网是如何连接建立的。
  以一个MDI应用程序的CMainFrame类为例:
  //in MaimFrm.h
  class CMainFrame : public CMDIFrameWnd
 {
 DECLARE_DYNAMIC(CMainFrame)
         ...//
 }

  //in MaimFrm.cpp
  IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
  ...//

  展开后得:
  //in MaimFrm.h
  class CMainFrame : public CMDIFrameWnd
 {
  public:
 static const CRuntimeClass classCMainFrame;
 virtual CRuntimeClass* GetRuntimeClass() const;
    ...//
 }

  //in MaimFrm.cpp
 
  AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame ={CMainFrame,sizeof(classCMainFrame  ),0xFFFF,NULL,(CRuntimeClass*)(&CMDIFrameWnd::classCMDIFrameWnd),NULL,NULL};

  CRuntimeClass* CMainFrame::GetRuntimeClass() const
  { return &CMainFrame::classCMainFrame; }
  有上面的代码可以看到IMPLEMENT_DYNAMIC宏初始化和实现了DECLARE_DYNAMIC宏为CMainFrame添加的CRuntimeClass classCMainFrame和CRuntimeClass* GetRuntimeClass() const静态函数。
其中最重要的一点是:   CMainFrame类的classCMainFrame对象的CRuntimeClass* m_pBaseClass被“间接的”赋值为&CMDIFrameWnd::classCMDIFrameWnd,也就是它基类的静态CRuntimeClass classCMDIFrameWnd成员。
以此类推一:
   CMDIFrameWnd::classCMDIFrameWnd的成员CRuntimeClass* m_pBaseClass被“间接的”赋值为
其基类&CFrameWnd::classCFrameWnd;
   ....
   ....
   CCmdTarget::classCCmdTarget的成员CRuntimeClass* m_pBaseClass被“间接的”赋值为&CObject::classCObject;
   CObject没有基类,那么CObject::classCObject的成员CRuntimeClass* m_pBaseClass被赋予什么值呢?看看what is the special runtime-class structure for CObject (no base class) in MFC Framework?

  注释:CMainFrame类的基类顺序以此是CMDIFrameWnd--〉CFrameWnd-->CWnd-->CCmdTarget-->CObject

//in Afx.h
// class CObject is the root of all compliant objects
 class AFX_NOVTABLE CObject
 {
   public:
   virtual CRuntimeClass* GetRuntimeClass() const;
   static const CRuntimeClass classCObject;
   ...//
 }
 
//in objcore.cpp
 // special runtime-class structure for CObject (no base class)
 const struct CRuntimeClass CObject::classCObject =
 { "CObject", sizeof(CObject), 0xffff, NULL, NULL, NULL };

 CRuntimeClass* CObject::GetRuntimeClass() const
 {
 return _RUNTIME_CLASS(CObject);
 }
 
                       ///
                       /* 一.4.执行期类型识别网的应用 */
                       //
    执行期类型识别网建立好了,它将在MFC Framework中发挥重要作用,这里举一个其应用的例子:
    CObject::IsKindOf函数就是利用该网完成的函数。下面看看其源代码:
  //in objcore.cpp
  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;
   #ifdef _AFXDLL
 for (;;)
   #else
 while (pClassThis != NULL)
   #endif
 {
  if (pClassThis == pBaseClass)
   return TRUE;
   #ifdef _AFXDLL
  if (pClassThis->m_pfnGetBaseClass == NULL)
   break;
  pClassThis = (*pClassThis->m_pfnGetBaseClass)();
   #else
  pClassThis = pClassThis->m_pBaseClass;
   #endif
 }
 return FALSE;       // walked to the top, no match
}

                       //
                       /*  二.1. 执行期动态创建技术   */
                       //
   谈完了执行期类型识别网建立后,就来看看另一项重要的技术-----执行期动态创建技术 ,该技术应用可谓是甚广,在你的SDI中的主框架,视图和其对应的文档以及MDI中的子框架,视图和其对应的文档都是利用动态创建技术生成的。
   执行期动态创建技术利用的也是CRuntimeClass结构和一对对应的宏DECLARE_DYNCREATE(class_name)
和IMPLEMENT_DYNCREATE(class_name, base_class_name);
   下面看看这两个宏的定义吧:

   #define DECLARE_DYNCREATE(class_name) /
 DECLARE_DYNAMIC(class_name) /
 static CObject* PASCAL CreateObject();
   #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, NULL)
   应该看出来执行期类型识别网是执行期动态创建技术的基础。
                    
                       ///
                       /* 二.2 执行期动态创建技术组成部分*/
                       ///
                      
   下面将列出与执行期动态创建技术有关的CRuntimeClass的结构成员:
    // object type information  
   struct CRuntimeClass
   {
       CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
       CObject* CreateObject();
       ...//
   };
  
                      
                       /*      二.3.宏展开       */
                                        
   这回我们以SDI程序中的CMainFrame的动态创建为例,下面看看上面的两个宏在xx.h和xx.cpp文件中展开后的样子:
   //in MainFrm.h
   class CMainFrame
   { 
      public:
        static const CRuntimeClass classCMainFrame;
 virtual CRuntimeClass* GetRuntimeClass() const;
        static CObject* PASCAL CreateObject();
        ...//
   };
   //in MainFrm.cpp
    CObject* PASCAL CMainFrame::CreateObject() { return new class_name; }
    AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame    ={
CMainFrame,sizeof(classCMainFrame),0xFFFF,CMainFrame::CreateObject,(CRuntimeClass*)(&CMDIFrameWnd::classCMDIFrameWnd),NULL,NULL};

    CRuntimeClass* CMainFrame::GetRuntimeClass() const
  { return &CMainFrame::classCMainFrame; }
    看出来着两个宏展开后与前面介绍的那两个宏的不同了吧;DECLARE_DYNCREATE在头文件中加入了一个函数static CObject* PASCAL CreateObject();IMPLEMENT_DYNCREATE的展开也有所不同。下面将详述。
 
 
                        
                         /* 二.4.如何进行动态创建  */
                         ///
    让我们看看CMainFrame的构造函数的access control吧!protected:疑惑了吧?受保护的构造函数是不能够得实例化的。那么CMainFrame是如何被构造出来的呢?这就是微软利用CRuntimeClass进行动态创建的原因。下面我用一个简单的例子来诠释一下构造方法:
   //in A.h
   class A
   {
      protected:A(){}
                ...//something else
      public:static A*CreateObject();
   };
   //in A.cpp
   A*A::CreateObject()
   {  return new A();}
   //in test.cpp
   int main(void)
   {
      A*pNewObj=A::CreateObject();//OK
      return 0;
   }
   诠释完了后就让我们一一对应的看看微软是如何做的吧,我们回头再看看DECLARE_DYNCREATE为CMainFrame
添加了什么吧。static CObject* PASCAL CreateObject(); 眼睛亮了吧!是的它就相当于上个例子中的 
static A*CreateObject();那么是谁调用了它呢?再回头看看IMPLEMENT_DYNCREATE作了什么吧?

AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame    ={
CMainFrame,sizeof(classCMainFrame),0xFFFF,CMainFrame::CreateObject,(CRuntimeClass*)(&CFrameWnd::classFrameWnd),NULL,NULL};
   原来该宏把m_pfnCreateObject赋值为CMainFrame::CreateObject;
   说到这你可能还是不很清楚,那么让我们看看MFC的代码吧!
    CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CMyDoc),
  RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
  RUNTIME_CLASS(CMyView));
    我们在MFC技术内幕系列之(二)----《MFC文档视图结构内幕 》中曾经说过,所以这里不详述,
    CSingleDocTemplate::OpenDocumentFile调用了CreateNewFrame
    CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)//部分源代码
    {
 ...//
       CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
        ...//
    }
    m_pFrameClass是CRuntimeClass*指针它指向CMainFrame::classCMainFrame;CreateNewFrame函数调用了
(CFrameWnd*)m_pFrameClass->CreateObject();而CObject* CRuntimeClass::CreateObject()代码如下:
                  
  //in objcore.cpp
  CObject* CRuntimeClass::CreateObject()
 {
 if (m_pfnCreateObject == NULL)
 {
  TRACE(traceAppMsg, 0,
   _T("Error: Trying to create object which is not ")
   _T("DECLARE_DYNCREATE /nor DECLARE_SERIAL: %hs./n"),
   m_lpszClassName);
  return NULL;
 }

 CObject* pObject = NULL;
 TRY
 {
  pObject = (*m_pfnCreateObject)();
 }
 END_TRY

 return pObject;
 }
  它调用了 (*m_pfnCreateObject)();即 CMainFrame::CreateObject();并返回了动态创建的 CMainFrame对象的指针。看到着你应该明白了吧。    
                       /
                       /*        三.下期预告         */
                       /
   MFC技术内幕系列之(四)-----《MFC消息映射与消息传递内幕》
 

这篇关于MFC技术内幕系列之(三)----MFC执行期类型识别与动态创建技术内幕的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/502024

相关文章

Python lambda函数(匿名函数)、参数类型与递归全解析

《Pythonlambda函数(匿名函数)、参数类型与递归全解析》本文详解Python中lambda匿名函数、灵活参数类型和递归函数三大进阶特性,分别介绍其定义、应用场景及注意事项,助力编写简洁高效... 目录一、lambda 匿名函数:简洁的单行函数1. lambda 的定义与基本用法2. lambda

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

MySQL 索引简介及常见的索引类型有哪些

《MySQL索引简介及常见的索引类型有哪些》MySQL索引是加速数据检索的特殊结构,用于存储列值与位置信息,常见的索引类型包括:主键索引、唯一索引、普通索引、复合索引、全文索引和空间索引等,本文介绍... 目录什么是 mysql 的索引?常见的索引类型有哪些?总结性回答详细解释1. MySQL 索引的概念2

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

Java获取当前时间String类型和Date类型方式

《Java获取当前时间String类型和Date类型方式》:本文主要介绍Java获取当前时间String类型和Date类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Java获取当前时间String和Date类型String类型和Date类型输出结果总结Java获取

Python实现PDF按页分割的技术指南

《Python实现PDF按页分割的技术指南》PDF文件处理是日常工作中的常见需求,特别是当我们需要将大型PDF文档拆分为多个部分时,下面我们就来看看如何使用Python创建一个灵活的PDF分割工具吧... 目录需求分析技术方案工具选择安装依赖完整代码实现使用说明基本用法示例命令输出示例技术亮点实际应用场景扩

SpringBoot改造MCP服务器的详细说明(StreamableHTTP 类型)

《SpringBoot改造MCP服务器的详细说明(StreamableHTTP类型)》本文介绍了SpringBoot如何实现MCPStreamableHTTP服务器,并且使用CherryStudio... 目录SpringBoot改造MCP服务器(StreamableHTTP)1 项目说明2 使用说明2.1

Qt如何实现文本编辑器光标高亮技术

《Qt如何实现文本编辑器光标高亮技术》这篇文章主要为大家详细介绍了Qt如何实现文本编辑器光标高亮技术,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录实现代码函数作用概述代码详解 + 注释使用 QTextEdit 的高亮技术(重点)总结用到的关键技术点应用场景举例示例优化建议

Python中图片与PDF识别文本(OCR)的全面指南

《Python中图片与PDF识别文本(OCR)的全面指南》在数据爆炸时代,80%的企业数据以非结构化形式存在,其中PDF和图像是最主要的载体,本文将深入探索Python中OCR技术如何将这些数字纸张转... 目录一、OCR技术核心原理二、python图像识别四大工具库1. Pytesseract - 经典O

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.