孙鑫vc++笔记.docx

上传人:b****5 文档编号:6353794 上传时间:2023-01-05 格式:DOCX 页数:30 大小:260.05KB
下载 相关 举报
孙鑫vc++笔记.docx_第1页
第1页 / 共30页
孙鑫vc++笔记.docx_第2页
第2页 / 共30页
孙鑫vc++笔记.docx_第3页
第3页 / 共30页
孙鑫vc++笔记.docx_第4页
第4页 / 共30页
孙鑫vc++笔记.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

孙鑫vc++笔记.docx

《孙鑫vc++笔记.docx》由会员分享,可在线阅读,更多相关《孙鑫vc++笔记.docx(30页珍藏版)》请在冰豆网上搜索。

孙鑫vc++笔记.docx

孙鑫vc++笔记

第三讲

有了一定的Windows32编程知识,就能学习MFC了。

在学习MFC之前,一定要弄明白Windows32编程中的消息循环是怎么回事。

MFC实际上就是对WindowsAPI函数的封装。

 

在Windows程序设计编程中,创建一个窗口要经历下面四个过程 (如有不懂,请看博客win32初窥)

1设计一个窗口类;

2注册窗口类;

3创建窗口;

4显示及更新窗口。

 

同样,MFC中也是一样的。

只是封装了而已,很多人认为MFC很难学,看着书本能编程序,但是却编写不了自己的程序,为什么呢?

因为还没有理解MFC的原理的机制。

市场上有很多有关VC++的书本,但是很少有将MFC原理讲得比较透彻的,引用孙鑫老师的话。

同样的,WinMain也是入口函数,具体的过程,请看孙鑫视频20讲之第三讲

其中,孙鑫老师开始提出的一个问题是:

在全局变量中,定义一个子类对象。

#include

#include

usingnamespacestd;

classPerson

{

public:

Person*p;

Person()

{

p=this;//保存this指针

}

virtualvoidsay()

{

cout<<"Person'ssay()"<

}

};

classStudent:

publicPerson

{

public:

Student()

{

}

voidsay()

{

cout<<"Student'ssay()"<

}

};

Students;

intmain()

{

s.p->say();//这里将调用子类的方法

return0;

}

什么都不用做,直接用向导生成一个MFC窗口。

下面是结构图,有五个类

是这样命名的,开头的C表示Class,C+工程名+(App,Doc,View...)

其中CMainFrame表示一个窗口(主窗体,包括标题栏,菜单...),XXApp表示运用程序,XXDOC表示文档类(加载数据,实现数据的存储与操作分离),XXView也表示一个窗口(相当于Windows的客户区)

 

下面看看MFC中类的主要继承关系:

由于继承树太大,这里只罗列了CWnd的继承关系

API中CreateWindowEx和CreateWindow几乎是一样的,Ex表示扩展,多了一个参数

下面用API模拟CWnd

just模拟

//下面是模仿封装API函数

classCWnd

{

public:

BOOLCreateEX(

DWORDdwExStyle,//extendedwindowstyle

LPCTSTRlpClassName,//registeredclassname

LPCTSTRlpWindowName,//windowname

DWORDdwStyle,//windowstyle

intx,//horizontalpositionofwindow

inty,//verticalpositionofwindow

intnWidth,//windowwidth

intnHeight,//windowheight

HWNDhWndParent,//handletoparentorownerwindow

HMENUhMenu,//menuhandleorchildidentifier

HINSTANCEhInstance,//handletoapplicationinstance

LPVOIDlpParam//window-creationdata

);

BOOLShowWindow(intnCmdShow);

BOOLUpdateWindow();

public:

HWNDm_hWnd;

};

BOOLCWnd:

:

CreateEx(

DWORDdwExStyle,//extendedwindowstyle

LPCTSTRlpClassName,//registeredclassname

LPCTSTRlpWindowName,//windowname

DWORDdwStyle,//windowstyle

intx,//horizontalpositionofwindow

inty,//verticalpositionofwindow

intnWidth,//windowwidth

intnHeight,//windowheight

HWNDhWndParent,//handletoparentorownerwindow

HMENUhMenu,//menuhandleorchildidentifier

HINSTANCEhInstance,//handletoapplicationinstance

LPVOIDlpParam//window-creationdata

);

{

m_hWnd=:

:

CreateWindowEx(dwExstyle,lClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,

hMenu,hInstance,lParam);

if(m_hWnd!

=NULL)

returnTRUE;

else

returnFALSE;

}

BOOLCWnd:

:

ShowWindow(intnCmdShow)

{

return:

:

ShowWindow(m_hWnd,nCmdShow);//调用的是SDK全局函数,加个:

:

说明是全局的

}

BOOLCWnd:

:

UpdateWindow()

{

return:

:

UpdateWindow(m_hWnd);

}

intWINAPIWinMain(

HINSTANCEhInstance,//handletocurrentinstance

HINSTANCEhPrevInstance,//handletopreviousinstance

LPSTRlpCmdLine,//commandline

intnCmdShow//showstate

{

WNDCLASSwndclass;

wndclass.cbClsExtra=0;

wndclass.cbWndExtra=0;

wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);

wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);

wndclass.hInstance=hInstance;

...

CWndcwnd;

cwnd.CreateEx(dwExstyle,...,...);

cwnd.ShowWindow(nCmdShow);

cwnd.UpdateWindow();

while(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

...

}

下面是重头戏(引用了某位仁兄的,太详细了)

重点:

MFC运行机制

  

  提示:

对于不想理解内部运行过程的,可以不看这一章,可以看了后面的界面设计再回头来看这一章,可能感觉更深刻。

  这一次课和上一次的课的重点就是MFC的窗口类创建过程,而要反复说明的就是:

MFC的程序和C语言的程序,从执行原理上说,是完全一致的。

  抓住这一点,那么对于理解MFC程序的运行机制也就相对于简单了。

  C中的main函数就相当于MFC中的WinMain函数。

  感兴趣的可以利用VC的断点设置自己跟踪下面讲述的各个函数,就明白它的执行顺序了。

  

  一、C语言程序执行步骤

  在C语言中,大约的步骤如下:

  1,全局变量内存分配

  2,进入main函数

  

  二、MFC程序的运行步骤(主要是初始化)

  

  打开一个MFCAPPWizard(exe)工程,跟踪其执行步骤,可以发现,是以下顺序:

  1)CXXApp中的全局变量定义

  CXXApptheApp;

  2)调用CXXApp构造函数

  CXXApp:

:

CXXApp(){}

  3)进入Winmain函数(_tWinMain为宏,值为WinMain)

  _tWinMain(){}

  

  4)完成初始化工作:

包括窗口类注册、窗口产生、显示和更新

  pThread->InitInstance()

  

  对于MFC程序,MainFrame,View,ToolBar,Controlbar等都是窗口,所以下面的窗口注册与创建、显示等要反复调用多次,一次对应一个窗口

  

  

(1)注册窗口类

  

  AfxEndDeferRegisterClass

  

  

(2)创建窗口

  

  CMainFrame:

:

PreCreateWindow()//反复调用一次是给我们修改窗口属性的机会

  

  CFrameWnd:

:

Create()

  

  (3)消息循环

  

  PumpMessage()

  

  补充1:

  

  在MFC中,由于涉及到(窗口)类定义,所以定义全局变量的时候,需要进行更多的步骤。

  

  全局变量涉及到类定义(类似于C中的类型定义)的话,那么需要遵循以下步骤(以MFC的窗口类为例)

  

  1)设计一个窗口类

  

  2)注册窗口类

  

  3)创建窗口

  

  4)显示及更新窗口

  

  5)消息循环

  

   

  

  补充2:

其他需要注意的几点

  

  1,每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。

在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。

在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。

  

  2,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。

(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。

  

  3,设计窗口类:

在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。

  

  4,PreCreateWindow()是个虚函数,如果子类有则调用子类的。

  

  5,CreateWindowEx()函数参数与CREATESTRUCT结构体成员完全一致,CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。

  

  6,注意两个函数。

  

  :

:

TranslateMessage(&m_msgCur)函数进行消息(如键盘消息)转换

  

  :

:

DispatchMessage(&m_msgCur)函数分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。

  

  7,可以认为View类窗口是CMainFram类窗口的子窗口。

DOCument类是文档类。

DOC-VIEW结构将数据本身与它的显示分离开。

  

  文档类用于数据的存储,加载;视类用于数据的显示,修改

  

  8,CTEApp:

:

InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。

语句如下:

  

  CSingleDocTemplate*pDocTemplate;

  pDocTemplate=newCSingleDocTemplate(

  IDR_MAINFRAME,

  RUNTIME_CLASS(CTEDoc),

  RUNTIME_CLASS(CMainFrame),//mainSDIframewindow

  RUNTIME_CLASS(CTEView));

  AddDocTemplate(pDocTemplate);//增加到模板

  

  补充3:

本课涉及到MFC函数的源文件位置

  

  根目录

  

  找到您安装VC98下MFC的位置,比如我的机子上为:

D:

\ProgramFiles\MicrosoftVisualStudio\VC98\MFC。

下面提供的就是相对路径了。

  

   

  

  CWinApp构造函数:

MFC=>SRC=>APPCORE.CPP

  AfxWinMain:

MFC=>SRC=>WINMAIN.CPP

  AfxEndDeferRegisterClass:

MFC=>SRC=>APPCORE.CPP

  CFrameWnd:

:

PreCreateWindow()函数所在文件:

MFC=>SRC=>WINFRM.CPP

  CFrameWnd:

:

Create()函数路径:

MFC=>SRC=>WINFRM.CPP

  CWnd:

:

CreateEx()函数路径:

MFC=>SRC=>WINCORE.CPP

  CWinThread:

:

Run()方法路径:

MFC=>SRC=>THRDCORE.CPP

 创建按钮

1在CMainFrame创建

双击CMainFrame,添加数据成员,CButtonm_btn

在OnCreate方法添加如下代码:

[html] viewplain copy

1.m_btn.Create(TEXT("first Button"),BS_PUSHBUTTON|WS_CHILD,CRect(0,0,100,100),this,123);  

2.m_btn.ShowWindow(SW_NORMAL);  

 

2在CXXView创建

双击CXXView,添加数据成员,CButtonm_btn

在CXXView右键AddwindowsMessageHandler添加WM_CREATE消息处理,然后生成OnCreate函数

在OnCreate函数添加如下代码

[html] viewplain copy

1.m_btn.Create(TEXT("Button2"),BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE,CRect(0,0,100,100),this,123);  

两个代码效果是等价的:

第四讲

1.在单文档中view挡在MainFrame的前面。

此时如果编写针对MainFrame的mouseClick事件,将不会有反应。

 

2.消息响应会在3处修改代码,1处是在头文件中,

//{{AFX_MSG(CDrawView)

afx_msgvoidOnLButtonDown(UINTnFlags,CPointpoint);

afx_msgvoidOnLButtonUp(UINTnFlags,CPointpoint);

afx_msgvoidOnMouseMove(UINTnFlags,CPointpoint);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

另一处是cpp文件的beginMessageMap和EndMessageMap之间,

BEGIN_MESSAGE_MAP(CDrawView,CView)

//{{AFX_MSG_MAP(CDrawView)

ON_WM_LBUTTONDOWN()

ON_WM_LBUTTONUP()

ON_WM_MOUSEMOVE()

//}}AFX_MSG_MAP

//Standardprintingcommands

ON_COMMAND(ID_FILE_PRINT,CView:

:

OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT,CView:

:

OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView:

:

OnFilePrintPreview)

END_MESSAGE_MAP()

最后是要有函数实现的代码。

voidCDrawView:

:

OnLButtonDown(UINTnFlags,CPointpoint)

{

//TODAddyourmessagehandlercodehereand/orcalldefault

m_ptOrigin=m_ptOld=point;

m_bDraw=TRUE;

CView:

:

OnLButtonDown(nFlags,point);

}

 

3.画线:

定义一个成员变量保存mouseDown的点m_Point

 1)API函数方法画线用HDC

 2)用CDC类成员函数画线。

此时别忘记ReleaseDC

 3)用CClientDC

 4)用CWindowDC,用它甚至可以整个屏幕区域画线。

下面是上面4种方法的代码

/*HDChdc;

hdc=:

:

GetDC(m_hWnd);

MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);

LineTo(hdc,point.x,point.y);

:

:

ReleaseDC(m_hWnd,hdc);必须成对使用。

*/

/*CDC*pDC=GetDC();

pDC->MoveTo(m_ptOrigin);

pDC->LineTo(point);

ReleaseDC(pDC);必须成对使用。

*/

//CClientDCdc(this);

/*CClientDCdc(GetParent());

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);此处不需要ReleaseDC,因为CClientDC会自动释放DC*/

//CWindowDCdc(this);

//CWindowDCdc(GetParent());

/*CWindowDCdc(GetDesktopWindow());//此时可以在整个屏幕上画线。

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);*/

/*CPenpen(PS_DOT,1,RGB(0,255,0));

CClientDCdc(this);

CPen*pOldPen=dc.SelectObject(&pen);

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);

dc.SelectObject(pOldPen);*/

 

 5)用Bitmap填充所画的矩形。

CBitmapbitmap;

bitmap.LoadBitmap(IDB_BITMAP1);

CBrushbrush(&bitmap);

CClientDCdc(this);

dc.FillRect(CRect(m_ptOrigin,point),&brush);

//CBRUSH:

:

FromHandle是静态成员函数,所以可以用下面的方法调用。

CBrush*pBrush=CBrush:

:

FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

CBrush*pOldBrush=dc.SelectObject(pBrush);

dc.Rectangle(CRect(m_ptOrigin,point));

dc.SelectObject(pOldBrush);

m_bDraw=FALSE;

 

 6)用其它颜色画线

CClientDCdc(this);

CPenpen(PS_SOLID,1,RGB(255,0,0));

CPen*pOldPen=dc.SelectObject(&pen);//选中红色画笔

if(m_bDraw==TRUE)

{

 dc.SetROP2(R2_BLACK);//设置绘画模式

 dc.MoveTo(m_ptOrigin);

 //dc.LineTo(point);

 dc.LineTo(m_ptOld);

 //dc.MoveTo(m_ptOrigin);

 dc.MoveTo(m_ptOld);

 dc.LineTo(point);

 //m_ptOrigin=point;

 m_ptOld=point;

}

dc.SelectObject(pOldPen);

4.MFC中隐式的包含了windows.h。

为什么?

因为在AFXV_W32.h文件中:

//ThisisapartoftheMicrosoftFoundationClassesC++library.

//Copyright(C)1992-1998MicrosoftCorporation

//Allrightsreserved.

在AFXWIN.h中

//Note:

WINDOWS.HalreadyincludedfromAFXV_W32.H

 

5.如何从句柄获得对象的指针?

答FromHandle

 

6.类的静态成员函数可以由类名直接调用,也可以由对象调用。

可以认为静态成员函数并不属于某个对象,它属于类本身。

程序运行伊始,即使没有实例化类的对象,静态成员函数和静态成员变量已然有其内存空间。

静态成员函数不能访问非静态成员变量!

静态成员变量必须在类的外部初始化。

当然如果并不打算用到静态成员变量,此时你可以不初始它。

 

7.理解代码区,数据区,堆,栈!

请见下面的简介:

对于一个进程的内存空间而言,可以在逻辑上分成3个部份:

代码区,静态数据区和动态数据区。

动态数据区一般就是“堆栈”。

“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。

进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。

一个堆栈可以通过“基地址”和“栈顶”地址来描述。

全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 英语考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1