深入浅出MFC学习笔记(第三章MFC六大关键技术之仿真类型识别,动态创建)
- 格式:doc
- 大小:118.51 KB
- 文档页数:21
MFC程序也有一个WinMain函数,这个函数是我们在编译链接的时候由链接器将WinMain函数链接进来的。
这个函数在APPMODUL.CPP中。
带有Afx的属于应用程序框架类的函数,应用程序框架是辅助我们生成应用程序的框架模型,这个框架模型把很多类或者类与类之间做了一个有机的集成提供给我们,我们可以根据框架模型提供的类库来设计我们自己的应用程序,Af即Application frame 看看这些类如何和我们WinMain函数关联到一起的?CmainFrame的名字是不会变的,其它的都是C+工程名+App/Doc/View命名。
class CTestApp : public CwinApp,可以看到CtestApp是从CwinApp派生出来的。
当我们双击CtestApp这个类时,我们可以看到VC++6.0上面显示我们进入了Test.h头文件中,当把CtestApp展开时,然后双击任何一个函数名,包括构造函数名,析构函数名…我们可以看到VC++6.0上面显示我们进入了Test.cpp源文件中。
且这种情况和我们直接从Fileview 资源卡中直接进入相应的头文件和源文件是一样的效果。
其它类也是相似的操作进入头文件和源文件。
不管是全局变量还是在全局对象CTestApp theApp;,它们都是在程序运行之前,也就是在入口函数WinMain函数加载之前,就已经为它们分配好了内存空间,作为全局函数,就要调用构造函数创建内存空间。
所以是先运行全局对象CTestApp theApp,调用它的构造函数,然后才运行WinMain。
为什么要定义一个全局对象呢?为什么要让它在WinMain之前完成呢?全局对象theApp又有什么作用?CtestApp是从CwinApp派生出来的,theApp是应用程序对象,是一个全局对象,每一个MFC程序当中有且仅有一个从CwinApp派生出来的类,也只能有一个应用程序类实例化的对象,它就表示了我们应用程序本身。
MFC六大关键技术(第四部分)——永久保存(串行化)先用一句话来说明永久保存的重要:弄懂它以后,你就越来越像个程序员了!如果我们的程序不需要永久保存,那几乎可以肯定是一个小玩儿。
那怕我们的记事本、画图等小程序,也需要保存才有真正的意义。
对于MFC的很多地方我不甚满意,总觉得它喜欢拿一组低能而神秘的宏来故弄玄虚,但对于它的连续存储(serialize)机制,却是我十分钟爱的地方。
在此,可让大家感受到面向对象的幸福。
MFC的连续存储(serialize)机制俗称串行化。
“在你的程序中尽管有着各种各样的数据,serialize机制会象流水一样按顺序存储到单一的文件中,而又能按顺序地取出,变成各种不同的对象数据。
”不知我在说上面这一句话的时候,大家有什么反应,可能很多朋友直觉是一件很简单的事情,只是说了一个“爽”字就没有下文了。
要实现象流水一样存储其实是一个很大的难题。
试想,在我们的程序里有各式各样的对象数据。
如画图程序中,里面设计了点类,矩形类,圆形类等等,它们的绘图方式及对数据的处理各不相同,用它们实现了成百上千的对象之后,如何存储起来?不想由可,一想头都大了:我们要在程序中设计函数store(),在我们单击“文件/保存”时能把各对象往里存储。
那么这个store()函数要神通广大,它能清楚地知道我们设计的是什么样的类,产生什么样的对象。
大家可能并不觉得这是一件很困难的事情,程序有能力知道我们的类的样子,对象也不过是一块初始化了存储区域罢了。
就把一大堆对象“转换”成磁盘文件就行了。
即使上面的存储能成立,但当我们单击“文件/打开”时,程序当然不能预测用户想打开哪个文件,并且当打开文件的时候,要根据你那一大堆垃圾数据new出数百个对象,还原为你原来存储时的样子,你又该怎么做呢?试想,要是我们有一个能容纳各种不同对象的容器,这样,用户用我们的应用程序打开一个磁盘文件时,就可以把文件的内容读进我们程序的容器中。
MFC六大关键技术是什么?1MFC程序的初始化过程2RTTI 动态类型标识3Dynamic Creation 动态生成4Persistence 永久保留5Message Mapping 信息映射6Message Routing 信息传递怎样自制RTTI?我们作为类库的设计者要在类构造起来的时候,记录必要的信息,以建立型录。
型录中的类的信息,最好以链表方式连接起来。
一般“类别型录”是一个结构,其中至少需要类名字,链表的Next 指针,以及链表的First指针。
First属于整体变量,一份就好,所以用static修饰。
为了将每一个类都能拥有成员变量集合,并且最好有一定的命名规则,然后经某种手段将整个类库构造好之后,“类别型录”(就是各个CRuntimeClass对象)则能呈现为:什么是DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC 宏?作用就是完成RTTI的“类别型录”。
为了将一个类对象塞到类之中,并定义一个可以捕捉到该对象地址的函数,定义一个宏为:#define DECLARE_DYNAMIC(class_name)public:static CRuntimeClass class##class_name;virtual CRuntimeClass* GetRuntimeClass()const;比如我使用了DECLARE_DYNAMIC(CView)编译器预处理器为我做出的代码是:public:static CRuntimeClass classCView;virtual CRuntimeClass * GetRuntimeClass()const;也就是定义类时,将类放入DECLARE_DYNAMIC宏就是把要放的对象放到了里边。
具体连接工作是由IMPLEMENT_DYNAMIC宏来实现:#define IMPLEMENT_DYNAMIC(class_name,base_class_name)_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)这里不做扩展,总之,IMPLEMENT_DYNAMIC内容不仅制定初值,它使用了一个struct AFX_CLASSINIT {AFX_CLASSINTI(CRuntimeClass * pNewClass);};(c++的struct和class都有构造函数):AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass*pNewClass){ pNewClass->m_NextClass = CRuntimeClass::pFirstClass; CRuntimeClass::pFirstClass = pNewClass;}就是上边的这个构造函数完成了连接工作。
MFC的六⼤机制MFC的六⼤机制程序的初始化过程运⾏时类型识别动态创建永久保存消息映射命令传递运⾏时类型识别MFC的运⾏时类型识别就是在程序运⾏过程中判断某个对象是否属于某个类,MFC通过为需要进⾏运⾏时类型识别的类添加⼀个静态CRuntimeClass类对象,其属于此类⽽不是属于某⼀特定对象,其在没有实例化对象的时候就已经存在并且可以被使⽤。
struct CRuntimeClass{//省略其他⽆关的成员LPCSTR m_lpszClassName; //类的名称int m_nObjectSize; //类的⼤⼩CRuntimeClass* m_pBaseClass; //指向基类的CRuntimeClass对象CRuntimeClass* m_pNextClass; //指向同类型对象的CRuntimeClass对象};然后提供了⼀个能返回类⾃⾝CRuntimeClass对象地址的函数GetRuntimeClass()CRuntimeClass* class_name::GetRuntimeClass() const/{ return &class_name::class##class_name;}CObject类提供⼀个IsKindof虚拟函数,此函数通过从对象⾃⼰的CRuntimeClass成员对象开始往基类遍历,判断是否有CRuntimeClass成员对象等于待判断类的CRuntimeClass成员对象,有则说明此对象属于此类(也就是说此对象是此类或其派⽣类的实例),⽆则说明不属于此类。
BOOL CObject::IsKindof(const CRuntimeClass *pClass) const{CRuntimeClass* pClassThis=GetRuntimeClass();while(pClassThis != NULL){if(pClassThis==pClass)return TRUE;pClassThis=pClassThis->m_pBaseClass;}return FALSE;}例如:IsKindof()最后就会返回TRUE,因为MFC⾃⼰定义的类都实现了运⾏时类型识别,⽽所有的类都是从CObject中派⽣的,所以CWnd类的对象实例属于CObject类。
3.MFC原理介绍包括C++封装原理和MFC六⼤关键技术,以及类向导和MFC应⽤程序向导的使⽤⽅法。
讲解MFC消息映射机制的原理、消息映射函数的建⽴,以及消息发送和接收的⽅法。
CTime类,C语⾔中time函数void CTmDlg::OnButton1(){CTime t = CTime::GetCurrentTime();int nYear = t.GetYear();int nMonth = t.GetMonth();int nDay = t.GetDay();int nHour = t.GetHour();int nMin = t.GetMinute();int nSec = t.GetSecond();int nWeek = t.GetDayOfWeek();//周三CString str;str.Format("当前时间是:%d年%02d⽉%02d⽇ %02d:%02d:%02d",nYear,nMonth,nDay,nHour,nMin,nSec);//AfxMessageBox(str);SetWindowText(str);//SetTimer}void CTmDlg::OnButton2(){time_t tt = time(NULL);tm * pTime = localtime(&tt);int nYear = pTime ->tm_year+1900;int nMonth = pTime ->tm_mon+1;int nDay = pTime ->tm_mday;int nHour = pTime ->tm_hour;int nMin = pTime ->tm_min;int nSec = pTime ->tm_sec;CString str;str.Format("当前时间是:%d年%02d⽉%02d⽇ %02d:%02d:%02d",nYear,nMonth,nDay,nHour,nMin,nSec);//AfxMessageBox(str);SetWindowText(str);//SetTimer}类// MyTime.h: interface for the CMyTime class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_)#define AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000//⾯向对象(C++)特点:抽象,封装和派⽣,(多态)#include <time.h>class CMyTime{time_t m_time;public:int GetYear() const;static CMyTime GetCurrentTime();CMyTime();~CMyTime();};#endif // !defined(AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_)// MyTime.h: interface for the CMyTime class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_)#define AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_#if _MSC_VER > 1000#pragma once#endif// _MSC_VER > 1000//⾯向对象(C++)特点:抽象,封装和派⽣,(多态)#include <time.h>class CMyTime{time_t m_time;public:int GetYear() const;static CMyTime GetCurrentTime();CMyTime();~CMyTime();};#endif// !defined(AFX_MYTIME_H__A7F50C7B_E6B7_4ED5_BB03_E00EB9935406__INCLUDED_)#include "stdafx.h"#include <time.h>#include <stdio.h>#include "MyTime.h"int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){CMyTime t = CMyTime::GetCurrentTime();int nYear = t.GetYear();//CTime time;/* time_t tt = time(NULL);tm * pTime = localtime(&tt);int nYear = pTime ->tm_year+1900;int nMonth = pTime ->tm_mon+1;int nDay = pTime ->tm_mday;int nHour = pTime ->tm_hour;int nMin = pTime ->tm_min;int nSec = pTime ->tm_sec;char s[200];sprintf(s,"当前时间是:%d年%02d⽉%02d⽇ %02d:%02d:%02d",nYear,nMonth,nDay,nHour,nMin,nSec);MessageBox(NULL,s,"提⽰",0);*/return0;}MFC所有封装类⼀共200多个,但是MFC的内部技术不只是简单的封装。
MFC六⼤核⼼机制MFC六⼤核⼼机制概述我们选择了C++,主要是因为它够艺术、够⾃由,使⽤它我们可以实现各种想法,⽽MFC将多种可灵活使⽤的功能封装起来,我们岂能忍受这种“⿊盒”操作?于是研究分析MFC的核⼼机制成为必然。
⾸先,列出要讲的MFC六⼤核⼼机制:1、MFC程序的初始化。
2、运⾏时类型识别(RTTI)。
3、动态创建。
4、永久保存。
5、消息映射。
6、消息传递。
本⽂讲第⼀部分,MFC程序的初始化过程。
简单的MFC窗⼝程序设计⼀个简单完整MFC程序,产⽣⼀个窗⼝。
当然这不能让AppWizard⾃动为我们⽣成。
我们可以在Win32 Application⼯程下⾯那样写:C++代码1 #include <afxwin.h>2class MyApp : public CWinApp3 {4public:5 BOOL InitInstance() //②程序⼊点6 {7 CFrameWnd *Frame=new CFrameWnd();//构造框架8 m_pMainWnd=Frame; //将m_pMainWnd设定为Frame;9 Frame->Create(NULL,"最简单的窗⼝");//建⽴框架10 Frame->ShowWindow(SW_SHOW); //显⽰框架11return true; //返回12 }13 };14 MyApp theApp; //①建⽴应⽤程序。
View Code设定链接MFC库,运⾏,即可看见⼀个窗⼝。
从上⾯,⼤家可以看到建⽴⼀个MFC窗⼝很容易,只⽤两步:⼀是从CWinApp派⽣⼀个应⽤程序类(这⾥是MyApp),然后建⽴应⽤程序对象(theApp),就可以产⽣⼀个⾃⼰需要的窗⼝(即需要什么样就在InitInstance()⾥创建就⾏了)。
整个程序,就改写⼀个InitInstance()函数,创建那么⼀个对象(theApp),就是⼀个完整的窗⼝程序。
第三章:MFC六大关键技术之仿真:类型识别深入理解MFC的内部运行原理,是本次学习《深入浅出MFC》的主要目的。
要模仿的六大技术包括:1:MFC程序的初始化过程。
2:RTTI(Runtime type identification)运行时类型识别。
3:Dynamic creation 动态创建4:Persistence永久保存5:消息映射6:消息传递。
RTTI(运行时类型识别)IsKindOf能够侦测某个对象是否属于某种类。
即判断某一对象所属的类是否是父类或当前类;要达到动态类型识别的能力,必须在构建类继承体系时记录必要的信息,这被称为类型型录表。
MFC以链表的方式建立了此表。
类型型录表的每个元素为CRuntimeClass类型,其定义为:1class CRuntimeClass23{45public:67LPCSTR m_lpszClassName;//对象所属类名89 Int m_nObjectSize;//对象大小1011UINT m_wSchema;//模式号1213 CObject *(PASCAL*m_pfnCreateObject)();//构建函数抽象类为NULL1415 CRuntimeClass *pBaseClasss;//基类CRuntimeClass对象指针。
1617 Static CRuntimeClass *pFirstClass;//链表头指针。
1819 CRuntimeClass *m_pNextClass;//下一指针。
2021};MFC使用此类作为每个类的成员变量。
使用宏定义为每个类定义了自己的CRuntimeClass成员变量。
DECLAR_DYNAMIC和IMPLENMENT_DYNAMIC宏使用这两个宏将CRuntimeClass对象不知不觉放到类之中。
DECLARE_DYNMIC宏定义如下:22#define DELCARE_DYNMIC ( class_name ) \2324public:\2526static CRuntimeClass class##class_name \2728virtual CRuntimeClass *GetRuntimeClass()const;##用来告诉编译器把两个字符串连接起来。
如果使用这个宏:DELCARE_DYNMIC(CView);那么预编译器将生成下列代码:29public:3031static CRuntimeClass classCView;3233virtual CRuntimeClass*GetRuntimeClass()const;以上代码仅仅是在类中定义CRuntimeClass对象,并定义一个返回CRuntimeClass对象地址的函数。
注意CRuntimeClass是static的,也就是说同一种类继承体系的对象共享一个CRuntimeClass对象。
初始化对象的内容以及建立类型型录表需要使用IMPLEMENT_DYNMIC宏。
34#define IMPLEMENT_DYNMIC (class_name,base_class_name)\3536_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL);3738_IMPLEMENT_RUNTIMECLASS又是一个宏,它定义如下:3940#define _IMPLEMENT_RUNTIMECLASS(class_name,\4142 base_class_name,wSchema,pfnNew)\4344static char _lpsz##class_name[]=#class_name;\4546 CRuntimeClass class_name::class##class_name=\4748 { _lpsz##class_name,sizeof(class_name),\4950 wSchema,pfnNew,\5152 RUNTIME_CLASS(base_class_name),NULL\5354 };5556static AFX_CLASSINIT _init##class_name \5758 ( & class_name::class##class_name);\5960 CRuntimeClass *class_name::GetRuntimeClass()const\6162 {\6364return &class_name::class##classname;\6566 }6768#define RUNTIME_CLASS(class_name)\6970 ( &class_name::class##class_name);AFX_CLASSINIT是一个类,看着跟宏定义似的,这样做很容易让人迷惑。
它用于将本节点连接到类型型录表,定义如下:71class AFX_CLASSINIT7273{7475public:7677 AFX_CLASSINIT(CRuntimeClass*pNewClass)//构造函数7879 {8081 pNewClass->m_pNextClass=CRuntime::pFirstClass;8283 CRuntimeClass::pFirstClass =pNewClass;8485 }8687};用法:88class CWnd:public CCmdTarget90{9192public:9394 DECLARE_DYNAMIC(CWnd);959697};IMPLEMENT_DYNMIC(CWnd,CCmdTarget);代码展开后为;98class CWnd:public CCmdTarget99100{101102public:103104static CRuntimeClass classCView;105106virtual CRuntimeClass*GetRuntimeClass()const107108109110};111112113114static char _lpszCWnd[]="CWnd";115116CRuntimeClass CWnd::classCWnd=117118{119120_lpszCView , sizeof(CWnd) , FFFF,NULL , &Wnd::classCWnd , NULL); 121122};123124static AFX_CLASSINIT _init_CWnd(&CWnd::classCWnd);126{127128 Return &CWnd::classCWnd;129130}定义宏的过程很复杂,但是一旦定义好之后,在使用时仅仅两句话就可以完成定义CRuntimeClass对象并且连接类型型录链表的工作。
CObject是所有类的基类,也是链表的头,此类应特别定义,不能在CObject内使用定义好的宏。
131class CObject132133{134135public:136137virtual CRuntimeClass*GetRuntimeClass()const;138139static CRuntimeClass classCObject;140141};142143static char szCobject[]="CObject";144145struct CRuntimeClass CObject::classCObject=146147{148149 szCObject ,sizeof(CObject),0xFFFF,NULL,NULL,NULL150151};152153static AFX_CLASSINIT _init_CObject(&Cobject::classObject);154155CRuntimeClass *CObject::GetRuntimeClass()const156157{158159return &CObject::classCObject;160161}由于CRuntimeClass对象是static成员变量,因此需要在类外初始化。
如果忘记初始化将会报链接错误。
CRuntimeClass*CRuntimeClass::pFirstClass=NULL;建好了类类型路表,要实现IsKindOf功能很容易。
首先在CObject加上一个IsKindOf函数,于是所有继承自此类的类都具有类型识别的功能。
能够将某个CRuntimeClass对象与类类型型录中的元素进行比较。
如:162class CObject163164{165166public:167168bool IsKindOf(const CRuntimeClass*pClass)const169170{171172 CRuntimeClass *pClassThis=GetRuntimeClass();173174while(pClassThis)175176 {177178if(pClassThis==pClass)179180return true;181182 pClassThis=pClassThis->m_pBaseClass;//沿着基类寻找。
183184 }185186return false;187188}189190};如果我们调用CWnd *cw=new CWnd;cw->IsKindOf(RUNTIME_CLASS(CFrameWnd));RUNTIME_CLASS实际就是&CFrameWnd::classCFrameWnd,它就是CFrameWnd的static的CRuntimeClass类型成员。
函数内利用GetRuntimeClass取得本类的CRuntimeClass对象的地址,即&CWnd::classCWnd,然后进行比较。
因为每一类型共用一个static的CRuntimeClass对象,因此属于同于类的CRuntimeClass对象的地址相同。
动态创建每一类的构建函数可以记录在类型别录中,当获得一个类名称,通过查找类别型录表找出对应的元素,然后调用其构建函数产生新对象。
在CRuntimeClass中m_pfnCreateObject即为构建函数首地址。
为了实现动态创建,需要添加两个宏:DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE。
如:191#define DECLARE_DYNCREATE(class_name)\192193 DECLARE_DYNCREATE(class_name)\194195static CObject *PASCAL CreateObject();196197#define IMPLEMENT_DYNCREATE (class_name,base_class_name)\ 198199 CObject*PASCAL class_name::CreateObject()\200201 {return new classname;};\202203 _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name, 204205 0xFFFF,class_name::CreateObject)以CFrameWnd为例,下列程序代码:206class CFrameWnd:public CWnd207208{209210public:211212 DECLEARE_DYNCREATE(CFrameWnd);213214};IMPLEMENT_DYNCREATE(CFrameWnd,CWnd);展开如下:215class CFrame:public CWnd216217{218219public:220221static CRuntimeClass classCFrameWnd;222223virtual CRuntimeClass *GetRuntimeClass()const;224225static CObject *PASCAL CreateObject();226227};228229CObject _PASCAL CFrameWnd::CreateObject()230231{232233return new CFrameWnd;234}235236static char _lpszCFrameWnd[]="CFrameWnd";237238CRuntimeClass CFrameClass::classCFrameWnd={239240_lpszCFrameWnd,sizeof(CFrameWnd),0xFFFF,CFrameWnd::CreateObject,RUNTIME_CALSS(CW nd),NULL};241242static AFX_CLASSINIT _init_CFrameWnd243244 (&CFrameWnd::classCFrameWnd);245246CRuntimeClass*CFrameWnd::GetRunimeClass()const247248{return &CFrameWnd::classCFrameWnd;}注意对象构建函数为static函数。