MFC六大核心机制之三:动态创建

来源:互联网 发布:担保行业 知乎 编辑:程序博客网 时间:2024/06/07 20:39


http://www.jizhuomi.com/software/271.html

   MFC中很多地方都使用了动态创建技术。动态创建就是在程序运行时创建指定类的对象。例如MFC的单文档程序中,文档模板类的对象就动态创建了框架窗口对象、文档对象和视图对象。动态创建技术对于希望了解MFC底层运行机制的朋友来说,非常有必要弄清楚。

       不需要手动实例化对象的疑惑

       MFC编程入门时,一般人都会有这样的疑惑:MFC中几个主要的类不需要我们设计也就罢了,为什么连实例化对象都不用我们来做?我们认为本该是:需要框架的时候,亲手写上CFrameWnd myFrame;需要视的时候,亲自打上CView myView;……。

       但MFC不给我们这个机会,致使我们错觉窗口没有实例化就弹出来了!但大伙想了一下,可能会一拍脑门,认为简单不过:MFC自动帮我们完成CView myView之流的代码不就行了么!其实不然,写MFC程序的时候,我们几乎要对每个大类进行派生改写。换句话说,MFC并不知道我们打算怎样去改写这些类,当然也不打算全部为我们“静态”创建这些类了。即使静态了创建这些类也没有用,因为我们从来也不会直接利用这些类的实例干什么事情。我们只知道,想做什么事情就往各大类里塞,不管什么变量、方法照塞,塞完之后,我们似乎并未实例化对象,程序就可以运行!

       CRuntimeClass链表

       要做到把自己的类交给MFC,MFC就用同一样的方法,把不同的类一一准确创建,我们要做些什么事情呢?同样地,我们要建立链表,记录各类的关键信息,在动态创建的时候找出这些信息,就象上一节RTTI那样!我们可以设计一个类:

C++代码
  1. struct CRuntimeClass{   
  2.        LPCSTR m_lpszClassName;                //类名指针  
  3.        CObject* (PASCAL *m_pfnCreateObject)();   //创建对象的函数的指针  
  4.        CRuntimeClass* m_pBaseClass;                         //讲RTTI时介绍过  
  5.        CRuntimeClass* m_pNextClass;            //指向链表的下一个元素(许多朋友说上一节讲RTTI时并没有用到这个指针,我原本以为这样更好理解一些,因为没有这个指针,这个链表是无法连起来,而m_pBaseClass仅仅是向基类走,在MFC的树型层次结构中m_pBaseClass是不能遍历的)  
  6.        CObject* CreateObject();                 //创建对象  
  7.        static CRuntimeClass* PASCAL Load();    //遍历整个类型链表,返回符合动态创建的对象。  
  8.        static CRuntimeClass* pFirstClass;        //类型链表的头指针    
  9. };  

      一下子往结构里面塞了那么多的东西,大家可以觉得有点头晕。至于CObject* (PASCAL *m_pfnCreateObject)();,这定义函数指针的方法,大家可能有点陌生。函数指针在C++书籍里一般被定为选学章节,但MFC还是经常用到此类的函数,比如我们所熟悉的回调函数。简单地说m_pfnCreateObject即是保存了一个函数的地址,它将会创建一个对象。即是说,以后,m_pfnCreateObject指向不同的函数,我们就会创建不同类型的对象。

      有函数指针,我们要实现一个与原定义参数及返回值都相同一个函数,在MFC中定义为:

      static CObject* PASCAL CreateObject(){return new XXX};//XXX为类名。类名不同,我们就创建不同的对象。

      由此,我们可以如下构造CRuntimeClass到链表(伪代码):

       CRuntimeClass classXXX={  
       类名,  
       ……,   
       XXX::CreateObject(),  //m_pfnCreateObject指向的函数  
       RUNTIME_CLASS(基类名), // RUNTIME_CLASS宏可以返回CRuntimeClass对象指针。  
       NULL                   //m_pNextClass暂时为空,最后会我们再设法让它指向旧链表表头。  
       };

       这样,我们用函数指针m_pfnCreateObject(指向CreateObject函数),就随时可new新对象了。并且大家留意到,我们在设计CRuntimeClass类对时候,只有类名(和基类名)的不同(我们用XXX代替的地方),其它的地方一样,这正是我们想要的,因为我们动态创建也象RTTI那样用到两个宏,只要传入类名和基类作宏参数,就可以满足条件。

       即是说,我们类说明中使用DECLARE_DYNCREATE(CLASSNMAE)宏和在类的实现文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏来为我们加入链表,至于这两个宏怎么为我们建立一个链表,我们自己可以玩玩文字代换的游戏,在此不一一累赘。但要说明的一点就是:动态创建宏xxx_DYNCREATE包含了RTTI宏,即是说, xxx_DYNCREATE是xxx_DYNAMIC的“增强版”。

MFC六大核心机制之三:动态创建

       到此,我们有必要了解一下上节课没有明讲的m_pNextClass指针。因为MFC层次结构是树状的,并不是直线的。如果我们只有一个m_pBaseClass指针,它只会沿着基类上去,会漏掉其它分支。在动态创建时,必需要检查整个链表,看有多少个要动态创建的对象,即是说要从表头(pFirstClass)开始一直遍历到表尾(m_pNextClass=NULL),不能漏掉一个CRuntimeClass对象。

       所以每当有一个新的链表元素要加入链表的时候,我们要做的就是使新的链表元素成为表头,并且m_pNextClass指向原来链表的表头,即像下面那样(当然,这些不需要我们操心,是RTTI宏帮助我们完成的):

C++代码
  1. pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;//新元素的m_pNextClass指针指向想加入的链表的表头。  
  2. CRuntimeClass::pFirstClass=pNewClass;//链表的头指针指向刚插入的新元素。  

       好了,有了上面的链表,我们就可以分析动态创建了。

       动态创建的步骤

       有了一个包含类名,函数指针,动态创建函数的链表,我们就可以知道应该按什么步骤去动态创建了:

       1、获得一要动态创建的类的类名(假设为A)。

       2、将A跟链表里面每个元素的m_lpszClassName指向的类名作比较。

       3、若找到跟A相同的类名就返回A所属的CRuntimeClass元素的指针。

       4、判断m_pfnCreateObject是否有指向创建函数,有则创建对象,并返回该对象。

       代码演示如下(以下两个函数都是CRuntimeClass类函数):

C++代码
  1. ///////////////以下为根据类名从表头向表尾查找所属的CRuntimeClass对象////////////  
  2.   
  3. CRuntimeClass* PASCAL CRuntimeClass::Load()   
  4. {   
  5. char szClassXXX[64];   
  6. CRuntimeClass* pClass;   
  7. cin>>szClassXXX;      //假定这是我们希望动态创建的类名  
  8. for(pClass=pFirstClass;pClass!=NULL;pClass=pClass->m_pNextClass)   
  9. {   
  10.      if(strcmp(szClassXXX,pClass->m_lpszClassName)==0)   
  11.      return pClass;   
  12. }   
  13.      return NULL;   
  14. }   
  15.   
  16. ///////////根据CRuntimeClass创建对象///////////   
  17. CObject* CRuntimeClass::CreateObject()   
  18. {   
  19.      if(m_pfnCreateObject==NULL) return NULL;   
  20.      CObject *pObject;   
  21.      pObject=(* m_pfnCreateObject)();              //函数指针调用  
  22.      return pObject;                                      
  23. }  

       有了上面两个函数,我们在程序执行的时候调用,就可以动态创建对象了。

       简单实现动态创建

       我们还可以更简单地实现动态创建,大家注意到,就是在我们的程序类里面有一个RUNTIME_CLASS(class_name)宏,这个宏在MFC里定义为:

       RUNTIME_CLASS(class_name)  ((CRuntimeClass*)(&class_name::class##class_name))

       作用就是得到类的RunTime信息,即返回class_name所属CRuntimeClass的对象。在我们的应用程序类(CMyWinApp)的InitInstance()函数下面的CSingleDocTemplate函数中,有:

       RUNTIME_CLASS(CMyDoc),

       RUNTIME_CLASS(CMainFrame),       // main SDI frame window

       RUNTIME_CLASS(CMyView)

       构造文档模板的时候就用这个宏得到文档、框架和视的RunTime信息。有了RunTime信息,我们只要一条语句就可以动态创建了,如:

       classMyView->CreateObject();      //对象直接调用用CRuntimeClass本身的CreateObject()

       总结

       最后再总结和明确下动态创建的具体步骤:

       1、定义一个不带参数的构造函数(默认构造函数);因为我们是用CreateObject()动态创建,它只有一条语句就是return new XXX,不带任何参数。所以我们要有一个无参构造函数。

       2、类说明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;和在类的实现文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏;这个宏完成构造CRuntimeClass对象,并加入到链表中。

       3、使用时先通过宏RUNTIME_CLASS得到类的RunTime信息,然后使用CRuntimeClass的成员函数CreateObject创建一个该类的实例。

       4、CObject* pObject = pRuntimeClass->CreateObject();//完成动态创建。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 不小心把打印机驱动删除了怎么办 蓝牙不小心点到忽略此设备怎么办 千牛工作台无线开店确认不了怎么办 开通诚信通后营业执照注销了怎么办 淘宝标的货跟发的不一样怎么办 wps表格里单元之间重叠了怎么办 淘宝店铺停了一段时间没了怎么办 转转买家收货为敲诈卖家怎么办 淘宝被投诉盗用官网图片怎么办 淘宝订单买下后卖家告知无货怎么办 盗图被删除还是待处理违规该怎么办 如果买家说你们的买家秀一样怎么办 花呗唤起安全核身验证失败怎么办 淘宝买东西花呗分期额度不够怎么办 支付宝余额支付额度已达上限怎么办 我是淘宝卖家遇到无良买家怎么办 我的保证金被淘宝当做违约金怎么办 淘宝顾客不想退货申请仅退款怎么办 被买家提供证明说我卖假货怎么办 淘宝退货快递公司填错了俩次怎么办 淘宝上退货把运单号写错了怎么办? 阿里巴巴发货了快递单号掉了怎么办 淘宝上买东西货物被物流扣留怎么办 淘宝买错了对方已发货怎么办 淘宝卖家发货的时候没货了怎么办 买了球显示未出票中奖了怎么办 点错了允许易企秀获得权限怎么办 淘宝还没发货商家拒绝退款怎么办 买家不补邮费还要你发货怎么办 天猫客服提示获取信息失败怎么办 拼多多下单了商家不发货怎么办 店铺扣2分宝贝降权了怎么办 淘宝店没订单没流量怎么办啊 淘宝id账号登录密码忘记了怎么办 淘宝网店铺授权客服联系不上怎么办 卖家发货买家查不到物流信息怎么办 物流信息到了但东西没到怎么办 淘宝详情已更改对方恶意投诉怎么办 淘宝店写的不给退换怎么办 从饿了么商家借款逾期怎么办 谷歌浏览器网页无法复制文字怎么办