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

相关文章

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

Pydantic中Optional 和Union类型的使用

《Pydantic中Optional和Union类型的使用》本文主要介绍了Pydantic中Optional和Union类型的使用,这两者在处理可选字段和多类型字段时尤为重要,文中通过示例代码介绍的... 目录简介Optional 类型Union 类型Optional 和 Union 的组合总结简介Pyd

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

使用PyTorch实现手写数字识别功能

《使用PyTorch实现手写数字识别功能》在人工智能的世界里,计算机视觉是最具魅力的领域之一,通过PyTorch这一强大的深度学习框架,我们将在经典的MNIST数据集上,见证一个神经网络从零开始学会识... 目录当计算机学会“看”数字搭建开发环境MNIST数据集解析1. 认识手写数字数据库2. 数据预处理的

Pytorch微调BERT实现命名实体识别

《Pytorch微调BERT实现命名实体识别》命名实体识别(NER)是自然语言处理(NLP)中的一项关键任务,它涉及识别和分类文本中的关键实体,BERT是一种强大的语言表示模型,在各种NLP任务中显著... 目录环境准备加载预训练BERT模型准备数据集标记与对齐微调 BERT最后总结环境准备在继续之前,确