深入浅出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原理第四讲.动态创建机制⼀⼂要学习的知识点以及简介 动态创建是什么意思? 动态创建其实就是跟C++的new⼀样.都是创建对象.但是规避了C++语法的缺陷.例如: char * ClassName = "CMyApp" CObject *obj = new ClassName;C++ 中不允许这样编写代码要学习的知识点 1.DECLARE_DYNCREATE 宏学过上讲RTTI的应该明⽩.这个就是个⽂字替换. 也可以说这个宏是⼀个声明宏当然也有实现宏 2.IMPLEMENT_DYNCREATE还需要了解CRuntimeClass 结构. ⽀持动态创建的成员.struct CRuntimeClass{LPCSTR m_lpszClassName; 类名int m_nObjectSize; 类⼤⼩UINT m_wSchema; 类编号CObject* (PASCAL* m_pfnCreateObject)(); 存放⽀持动态创建的类CRuntimeClass* m_pBaseClass;// OperationsCObject* CreateObject(); 动态创建函数BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; 判断函数...CRuntimeClass* m_pNextClass; 链表存储执向下⼀个};⼆⼂如何使⽤动态创建. ⽀持动态创建的是跟窗⼝有关的. 也就是CFrameWnd类. 如果我们继承了这个类.我们也可以让他⽀持动态创建. 1.添加声明宏 2.添加实现宏 3.动态创建使⽤.添加声明宏则添加到我们继承CFrameWnd类中即可. 实现宏则在外边参数填写⾃⼰的类名添加实现宏使⽤动态创建. 在InitInstance⾥⾯使⽤即可.我们的new 窗⼝改成动态创建即可.RUNTIME_CLASS宏可以拆解开.m_pMainWnd = (CMainWnd *)((CRuntimeClass*)(&CMainWnd::classCMainWnd))->CreateObject();应⽤程序实现截图:三⼂动态创建实现原理之宏拆开 我们要看实现原理.当然要把宏拆看看看做了什么事情了.1. DECLARE_DYNCREATE 宏拆开#define DECLARE_DYNCREATE(class_name) \DECLARE_DYNAMIC(class_name) \ RTTI动态识别static CObject* PASCAL CreateObject();我们可以看到这个宏包含了我们的RTTI 类型识别. 并且添加了⼀个新的成员函数static Cobject * Createobject();RTTI动态识别.上⼀讲已将讲过了. 就是添加了⼀个 CRuntimeClass 成员.以及获取成员的⽅法. 本次不讲解. 全部解开的宏public:static const CRuntimeClass classCMainWnd; 指针virtual CRuntimeClass* GetRuntimeClass() const; 获取这个指针的函数static CObject* PASCAL CreateObject(); 新增的函数2.实现宏拆开 IMPLEMENT_DYNCREATE#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)其中还包含了⼀个宏 IMPLEMENT_RUNTIMECLASS也进⾏拆开.#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); }第⼀个宏 IMPLEMENT_DYNCREATE 其实就是对 CreateObject进⾏实现. 也就是返回⾃⼰本⾝的创建的类第⼆个宏就是对⾃⼰本⾝的结构体成员进⾏初始化.并且实现虚函数获取⾃⼰的这个成员. 跟RTTI⼀样.只不过初始化的时候.结构体初始化的值不⼀样.也就是我们上⾯说的CRuntimeClass结构中的新增的两个成员进⾏了赋值.解析的代码//IMPLEMENT_DYNCREATE(CMainWnd,CFrameWnd)CObject* PASCAL CMainWnd::CreateObject(){return new CMainWnd;}const CRuntimeClass CMainWnd::classCMainWnd ={"CMainWnd", sizeof(class CMainWnd), 0xFFFF, CMainWnd::CreateObject, 添加了⾃⼰的成员函数指针.其余地⽅⼀样这个函数指针创建⾃⼰本⾝对象并且返回.RUNTIME_CLASS(CFrameWnd), NULL, NULL};CRuntimeClass* CMainWnd::GetRuntimeClass() const{return RUNTIME_CLASS(CMainWnd);}所以远离就是CRuntime⾥⾯添加⾃⼰创建对象的函数指针. 然后⾃⼰的类中实现这个函数.创建⾃⼰的对象并且返回这个就是动态创建了.。
软件英才网软件行业驰名招聘网站命令传递(Command routing)消息如果是仅仅从派生类流向父类,那就非常简单了。
然而MFC用来处理消息的C++类,并不是单线发展的。
document/view也具有处理消息的能力。
因此,消息应该有横向流动的机会。
MFC对消息循环的规定为:1:若是一般的windows消息(WM_xx)则一定是由派生类流向基类。
2:如果是WM_COMMAND消息,就非常复杂了。
要区分接受者的类型:1:接受者若为Frame窗口:处理次序为:View-> Frame窗口本身->CWinApp类。
2:接受者若为View :处理次序为:View本身->Document;3:接受者若为Document:处理次序为:Document本身->Document template软件英才网软件行业驰名招聘网站因此,接下来我们的任务就是仿真以上的消息传递路线。
以下为需要添加的函数:全局函数AfxWndProc,它是整个消息循环的起始点,本来应该在CWinThread::Run中被调用,每调用一次就推送一个消息。
模拟windows的disPatch函数。
软件英才网软件行业驰名招聘网站LRESULT AfxWndPro(HWND hWnd,UINT nMsg,WPARAMwParam,LPARAM lParam,CWnd *pWnd) {cout<<"AfxWndProc()"<<endl;return AfxCallWndProc (pWnd,hWnd,nMsg,wParam,lParam); }LRESULT AfxCallWndProc(CWnd*pWnd,HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)软件英才网软件行业驰名招聘网站{cout<<"AfxCallWndProc"<<endl;LRESULT lResult=pWnd->windowProc(nMsg,wParam,lParam);return lResult;}全局函数AfxCallWndProc用于调用接受消息的类的消息处理函数。
//1、将C语言变量简单重新定义UINT、INT(32位,4字节)LONG、DWORD(32位,4字节)WPARAM、LPARAM(32位,4字节)SHORT、WORD(16位,2字节)LONG、LRESULTBOOL(TRUE、FALSE)PINTPSTR、LPSTR、LPCSTR(字符串指针,只读和可写)//2、H开头,Windows句柄类型变量HANDLE of Windows =>HWNDHANDLE of Instance =>HINSTANCEHANDLE of Icon =>HICON//3结构体类型SIZE、POINT、RECTMFC所有封装类一共有200多个,但是MFC的内部技术不只是简单地封装。
MFC内部总共有六大关键技术,构架起了整个MFC开发平台。
一、MFC的六大关键技术包括:a)MFC程序的初始化过程:b)消息映射机制;c)运行时类型识别(RTTI);d)动态创建;e)永久保存;f)消息传递;六大关键技术的目的是为了提高开发效率,开发者只需要在局部做简单的修改,即可处理大部分窗口事物。
二、SendMessage和PostMessage函数的功能:a)能够向指定的窗口内发送窗口消息,既可以是本进程内窗口也可以是其他进程的;b)既可以发送系统内部消息,消息编号的范围是:1-WM_USER-1;例如:WM_LBUTTONDONW,WM_MOUSEMOVE等;c)也可以发送非系统消息(开发者定义的消息),范围是WM_USER-0x7FFF。
三、SendMessage和PostMessage两个函数的区别是:a)SendMessage是阻塞型函数,PostMessage是非阻塞型函数:SendMessage用于调用指定窗口的内部程序,直到窗口程序处理完成以后再返回:PostMessage是将一个消息寄送到一个窗口内的消息队列后就立即返回。
b)两个函数的返回值不同:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM,lParam);BOOL PostMessage(HWND hWnd,UNIT Msg,WPARAM wParam,LPARAM,lParam); SendMessage的返回值依赖于消息处理函数c)跨线程或者跨进程发送消息,推荐使用PostMessage函数。
MFC原理系列报告之:运行期类型识别一总论刚学习完MFC,一时心血来潮,分析下MFC的几个关键技术,算是对学习的总结。
由于本人还是个初学者,能力有限,语言组织欠妥,肯定有不足之处,欢迎批评斧正。
有以下几个部分内容:1.RTTI(运行期类型识别)2.动态创建3.序列化的实现4.程序的产生,运行和结束,以单文档框架程序为例5.单文档框架程序/类型之间的关系6.MFC消息机制的流转7.实现自己的SPY ++二: RTTI, 动态创建,序列化RTTI是动态创建的基础,序列化同时用到动态创建和RTTI,所以RTTI 是MFC的基石。
三者的关系很紧密。
.1.动态创建可以简单理解为,通过字符串创建对象。
比如你封装了一个类CLine,如何仅仅通过字符串“CLine”创建来CLine类的对象。
2.序列化是指从持久存储介质中读出或写入一个对象的过程。
举个例子:在一个CAD系统里,用户画了一些线条呀,矩形等等。
如何将当前所有图形保存到文件中,下次打开文件,然后正确读取并显示出来。
让我们尝试分析下,假设有两个类CLine, CRectangle分别负责绘制线条和矩形。
CLine里保存线条的起始坐标点。
Crectangle保存矩形的左上角坐标点,以及右下角坐标点。
用户每画一个线条,则创建一个CLine对象,矩形也是如此。
当用户保存线条到文件时,需要保存哪些内容呢?肯定要有CLine的类型信息,类信息至少有类的名字。
如果不保存,读取图形文件时,无法创建一个CLine对象,也就不能再正确显示出来。
读出对象时,根据字符串“CLine”创建一个CLine对象,即动态创建。
这里保存和读取对象的过程就是序列化,很显然序列化需要动态创建的支持,但是二者共同基础却是RTTI。
三. RTTI详解RTTI (Run_Time Type Identification),即运行期类型识别,是MFC动态创建的基础,可以对类型进行管理,对类型之间的关系,对象的类型进行识别和检查。
Windows编程一、#define的几个注意点①#与##的用法;#xxx将后面的参数xxx字符串化xxx##yyy,将两个参数连接②\ 的用法一行结束使用,表示一行未结束。
二、函数调用约定_stdcall_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,堆栈由它自己清理。
在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见。
相对应的_cdecl,堆栈由main()函数或者其他函数清理。
C和C++程序的缺省调用方式则为__cdecl,下图为VC++6.0的默认设置,因此在不显式写明调用约定的情况下,一般都是采用__cdecl方式,而在与Windows API打交道的场景下,通常都是显式的写明使用__stdcall,才能与Windows API保持一致。
另外,还要注意的是,如printf此类支持可变参数的函数,由于不知道调用者会传递多少个参数,也不知道会压多少个参数入栈,因此函数本身内部不可能清理堆栈,只能由调用者清理了。
三、防止头文件重复包含----预编译在写好的类的首位加上预编译代码,例如:#ifndef xxx_h#define xxx_hClass xxx{...};#endif四、HDC、CDC、CClientDC、CWindowDCHDC是平台SDK提供的全局类,与设备上下文相关CDC则是类似于封装在CWnd中的一个HDC。
CClientDC:继承于CDC,构造函数完成获取DC,析构函数完成释放DC。
CWindowDC:继承于CDC,构造函数完成获取DC,析构函数完成释放DC,在整个窗口上绘图CMetaFileDC:图元文件设备描述环境类创建:CMetaFileDC dc;dc.Create();接下来用一般dc的绘图操作,绘图的内容均会保存至图元文件中;HMETAFILE m_hMetaFile=dc.Close(); //图元文件赋予数据成员显示图元文件:用一般dc的PlayMetaFile(m_hMetaFile)显示图元文件窗口销毁时删除图元文件SDK函数::DeleteMetaFile(m_hMetaFile)五、OnDraw函数、OnCreate函数OnDraw函数:窗口重绘的时候被框架类FrameWnd调用,响应WM_PAINT消息。
第三章: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;##用来告诉编译器把两个字符串连接起来。
第三章: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函数。