VC++深入详解学习记录

来源:互联网 发布:全球erp软件排名 编辑:程序博客网 时间:2024/06/16 06:34
1课 Windows程序内部运行机制

1.MFC生成的C++ 源文件中都有 StdAfx.h,此文件包含了常用的AFX函数的声明,其中有 afxwin.h,此文件包含了CRECT,CPoint CWnd等许多类及其方法的声明。
2.Project->Setting->Debug 可以加入命令行参数。
3.SDK 中要加入 "windows.h"stdio.h 。因为LoadCursor,MessageBox等函数的声明在这个文件中。
4.创建一个完整的窗口的四个步骤 SDK1 设计窗口类, 2注册窗口类,3创建窗口, 4显示窗口
5.函数名可以代表函数代码的首地址,即可作为函数指针。
6.要查看VC 数据类型,可以在 MSDN中输入“BOOL ”然后选择“ DATA TYPE”。
7.atof将字符串转化为float,atoi将字符串转化为 int型。
8.所有从CWnd 类派生的类都有 m_hWnd句柄。
9.变量的生命周期:可以认为出了包含它的大括号,这个变量的生命周期结束。所以全局变量的声明位于所有大括号之外。但是用 new声明的变量和用static声明的变量除外。
10.SDK示范程序,见下面。
11.sprintf格式化字符,其头文件为 stdio.h,MFC 中格式化字符用 CString.Format
12.GetDC()ReleaseDC() 要成对使用,否则会内存泄漏。同样, BeginPaint()EndPaint() 
13.GetStockObject() 得到画笔、画刷、字体、调色板的句柄,使用时必须用类型转换。
14.什么时候用NULL,什么时候用 0.答,对指针赋值时用NULL,对变量赋值时用 0.
15.什么是野指针?答:将指针指向的变量的内存释放后,此指针即变成野指针!如何避免野指针?答:将此指针指向 NULL即可。p=NULL;
16.SDK代码流程:
#include "windows.h"// 包含头文件 LoadCursor,TextOut等函数
#include "stdio.h"// 包含sprintf,printf等函数
LRESULT CALLBACK MyProc(...);// 声明回调函数
int WINAPI WinMain()
{
WNDCLASS wndcls;// 设计窗口类
wndcls.hcursor=LoadCursor();// 初始化
....
RegisterClass(&wndcls);// 注册窗口类
hwnd=CreateWindow(...);// 创建窗口
ShowWindow(..);// 显示窗口
UpdateWindow(..);
MSG msg;//定义消息结构体
while(GetMessage(...))// 消息循环
{
...
}
return 0;
}
LRESULT CALLBACK MyProc(...)// 实现回调函数
{
switch(uMsg)
{
case WM_CHAR:
break;
...
}
}

2课 掌握C++

1.定义结构体和类时别忘记在最后加入 "" 号!例如 Class Point{int x;int y;};
2.#include <xxx.h> #include "xxx.h"的区别: <>不查找运行时目录,""查找运行时目录!
3.类的定义中,如果未指明成员类型,则缺省为 private.而结构体中则缺省为public.
4.引用:引用经常用在函数的传参上。另外数值交换函数也经常用引用。例
change(int &x,int &y){int temp;temp=x;x=y;y=x} 调用时即可以用 int a=3;int b=4;change(a,b);一般不用指针来作为参数进行数值交换。因为会引起歧义。
5.通常将类的定义放.h文件,而将其实现放在 cpp文件中,别忘记了在cpp文件中 #include "xxx.h"
6.如何防止类的重复定义?
#inndef Point_H_H
#define Point_H_H
class Point{};
#endif来防止
7.源文件cpp 文件单独编译成 obj文件。最后由链接器将与将要使用到的 C++标准库类链接成exe文件,头文件不参加编译。所以在 cpp文件中别忘记了加入#include "xxx.h"
8.函数的覆盖,在子类中重写父类的函数,此时采用早期绑定的方法。如果加入了 virtual,则将采用迟绑定的技术,在运行时根据对象的类型确定调用哪一个函数。此迟绑定技术是 MFC的类的继承的精髓。
9.强制类型转换。如果CFish CAnimal派生而来。则可以将鱼的对象转换为 CAnimal的对象,而反之则不行。从现实中理解也是正常的,鱼可以是动物,而动物却不是鱼。再如 int可以强制转换成char型。而反之则出错。
10 包含头文件时,<> ””是不同的。<>表示编译器从系统目录下开始搜索,然后再搜索 PATH环境变量所列出的目录,不搜索当前目录,找不到就出错。而 ””则表示先从当前目录搜索,然后才是系统目录和 PATH环境变量列出的目录。所以,如果头文件在系统目录下,就用 <>,如果头文件在当前目录下,就用 ””,这样可以加快搜索速度。
11 在类的头文件(*.h)的开头,一般定义有如下宏:
#ifndef ANIMAL_H_H
#define ANIMAL_H_H
Class ***
……
#endif
这么做就是为了避免类重复定义。

3课 MFC框架程序

1.main WinMain之前,全局变量(对象)已经被分配内存并初始化了。
2.MFC 中在WinMain之前有个 theApp全局变量先被构造并被初始化,而由于子类构造函数执行前,其父类的构造函数先被执行,所以 CTestApp的父类CWinAPP 的构造函数先执行。产生了 theApp对象后,在WinMain()中的指针 *pThread*pApp 就有了内容。
知识点: Afx前缀的函数代表应用程序框架 (Application Framework)函数,都是全局函数,在程序的任何地方都可以调用它。
3.MFC大致流程:
CTestApp theApp;// 构造全局对象
WinMain()
{
AfxWinMain();// 调用下面的函数
}
AfxWinMain()
{
pThread->Initinstance();// 初始化工作和注册窗口类,窗口显示和更新
pThread->Run();// 消息循环
}
而在 BOOL CTestApp::InitInstance()中的代码
 CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CTestDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CTestView));
 AddDocTemplate(pDocTemplate);
完成了将这三个类关联起来的工作。
4.如何在单文档文件中显示一个 CButton的对象?
CMainFrame::OnCreate()中定义一个 CButton的对象btn; 然后调用 btn.Create("维新",WS_DISABLED   |WS_CHILD | WS_VISIBLE | BS_AUTO3STATE,
  CRect(0,0,300,100),/*GetParent(),*/this,123);
注意点:
     (1). 此处btn不能是局部变量,否则它的生命周期太短,将不能显示。
     (2). create函数的第二个参数中加入 WS_VISIBLE 参数才行。否则必须调用 ShowWindow
也可以在 viewOnCreate 消息响应函数中加入
     (3).CButton 类的定义头文件在 afxwin.h中,而stdafx.h 包含了afxwin.h,所以可以直接使用。因为 MFC中的每一个类中都有#include "stdafx.h"的声明。
5 一个单文档窗口(多文档),标题栏和菜单栏位于 MainFrame的非客户区,而工具栏位于 MainFrame的客户区。而视图位于MainFrame的客户区。

4课 简单绘图

1.在单文档中view挡在 MainFrame的前面(View- 墙纸,MainFrame- )。此时如果编写针对MainFrame mouseClick事件,将不会有反应。

2.消息响应会在3处修改代码,
1)在头文件中,
//{{AFX_MSG(CDrawView)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
2 cpp文件的begin MessageMap End MessageMap之间,
BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
3)最后是要有函数实现的代码。
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TOD Add your message handler code here and/or call default
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种方法的代码
(1)
/*HDC hdc;
hdc=::GetDC(m_hWnd);
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
LineTo(hdc,point.x,point.y);
::ReleaseDC(m_hWnd,hdc); 必须成对使用。 */
(2)
/*CDC *pDC=GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC); 必须成对使用。 */
(3)
//CClientDC dc(this);
/*CClientDC dc(GetParent());
dc.MoveTo(m_ptOrigin);
dc.LineTo(point); 此处不需要 ReleaseDC,因为CClientDC 会自动释放 DC*/
(4)
//CWindowDC dc(this);
//CWindowDC dc(GetParent());
/*CWindowDC dc(GetDesktopWindow());// 此时可以在整个屏幕上画线。
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);*/
/*CPen pen(PS_DOT,1,RGB(0,255,0));
CClientDC dc(this);
CPen *pOldPen=dc.SelectObject(&pen);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);*/
  5)Bitmap 填充所画的矩形。
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
CBrush brush(&bitmap);
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);
//CBRUSH::FromHandle 是静态成员函数,所以可以用下面的方法调用。
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//NULL_BRUSH表示透明画刷,画出来的图形的背景就是透明的
CBrush *pOldBrush=dc.SelectObject(pBrush);
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);
m_bDraw=FALSE;
  6)用其它颜色画线
CClientDC dc(this);
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen *pOldPen=dc.SelectObject(&pen);// 选中红色画笔
if(m_bDraw==TRUE)
{
  dc.SetROP2(R2_NOTXORPEN);// 设置绘画模式,R2_NOTXORPEN可用来绘制橡皮线
  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文件中:
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
AFXWIN.h
// Note: WINDOWS.H already included from AFXV_W32.H

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

6.类的静态成员函数可以由类名直接调用,也可以由对象调用。可以认为静态成员函数并不属于某个对象,它属于类本身。程序运行伊始,即使没有实例化类的对象,静态成员函数和静态成员变量已然有其内存空间。静态成员函数不能访问非静态成员变量!静态成员变量必须在类的外部初始化。当然如果并不打算用到静态成员变量,此时你可以不初始它。

7.理解代码区,数据区,堆,栈!
请见下面的简介:
http://www.downcode.com/server/j_server/J_1010.Html
对于一个进程的内存空间而言,可以在逻辑上分成 3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈 (stack)”和“堆(heap) ”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

5课 文本编程

1.CWnd::CreateSolidCaret创建插入符, ShowCaret()显示插入符。
GetTextMetrics(),获得当前字体的一些信息。 CWnd::CreateCaret()创建图象插入符
 bitmap.LoadBitmap(IDB_BITMAP1);// 此处的bitmap为成员变量!!!
 CreateCaret(&bitmap);
 ShowCaret();
 TEXTMETRIC tm;// 字体结构体
 dc.GetTextMetrics(&tm);//
 m_ptOrigin.y+=tm.tmHeight;// 获得字体高度。
2.VCCString::LoadString(ID ),比较方便。
3.路径层的概念:有两种方法创建路径层:
  1 
 pDC->BeginPath();
 pDC->Rectangle(50,50,50+sz.cx,50+sz.cy);
 pDC->EndPath();
 pDC->SelectClipPath(RGN_DIFF);
   2
        CSize sz=pDC->GetTextExtent(str);
        CRgn rn;
        rn.CreateRectRgn(0,50,sz.cx,sz.cy);
        pDC->SelectClipRgn(&rn,RGN_DIFF);
路径层有什么作用?可以保护我们先前的文本或者图像不被后来画的覆盖。
4.View 上输入文字的步骤。
 CFont font;// 创建字体对象
 font.CreatePointFont(300," 华文行楷 ",NULL);//设置
 CFont *pOldFont=dc.SelectObject(&font);// 将字体选择到 DC
 TEXTMETRIC tm;// 创建字体信息对象
 dc.GetTextMetrics(&tm);// 获得当前字体信息
 if(0x0d==nChar)// 处理回车键
 {
  m_strLine.Empty();
  m_ptOrigin.y+=tm.tmHeight;
 }
 else if(0x08==nChar)// 处理退格键
 {
  COLORREF clr=dc.SetTextColor(dc.GetBkColor());
  dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
  m_strLine=m_strLine.Left(m_strLine.GetLength()-1);
  dc.SetTextColor(clr);
 }
 else
 {
  m_strLine+=nChar;
 }
 CSize sz=dc.GetTextExtent(m_strLine); CPoint pt;// 处理光标的位置
 pt.x=m_ptOrigin.x+sz.cx;
 pt.y=m_ptOrigin.y; SetCaretPos(pt);
 dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);// 输出字体 dc.SelectObject(pOldFont);//将原先的字体选择回去。
5.模拟卡啦OK 变色的步骤。
   1)设置定时器
   2)在定时器中加入如下代码
//DEL  m_nWidth+=5;// 此为view的成员变量,初始值为 0
//DEL
//DEL
//DEL  CClientDC dc(this);
//DEL  TEXTMETRIC tm;
//DEL  dc.GetTextMetrics(&tm);
//DEL  CRect rect;
//DEL  rect.left=0;
//DEL  rect.top=200;
//DEL  rect.right=m_nWidth;
//DEL  rect.bottom=rect.top+tm.tmHeight;// 此长方形的长度随着定时器的触发,逐渐增大
//DEL
//DEL  dc.SetTextColor(RGB(255,0,0));
//DEL  CString str;
//DEL  str.LoadString(IDS_WEIXIN);
//DEL  dc.DrawText(str,rect,DT_LEFT); 此函数的作用是将字符串输出到长方形中,但如果字符串的长度超过长方形的长度,多余的字符将被截断
//DEL
//DEL  rect.top=150;
//DEL  rect.bottom=rect.top+tm.tmHeight;
//DEL  dc.DrawText(str,rect,DT_RIGHT);
//DEL
//DEL  CSize sz=dc.GetTextExtent(str); 获得字符串的长度
//DEL  if(m_nWidth>sz.cx) 当长方形的长度大于字符串的长度后,将其重新归 0
//DEL  {
//DEL   m_nWidth=0;
//DEL   dc.SetTextColor(RGB(0,255,0));
//DEL   dc.TextOut(0,200,str);
//DEL  }
//DEL
//DEL  CView::OnTimer(nIDEvent);
6.SetTimer也可以用回调函数来操作,但并不方便。以下是步骤
  1   ViewOnCreate 消息响应函数中: SetTimer(1,1000,Timer2Proc);
  2  回调函数的实现:
void CALLBACK EXPORT Timer2Proc(
   HWND hWnd,      // handle of CWnd that called SetTimer
   UINT nMsg,      // WM_TIMER
   UINT nIDEvent,   // timer identification
   DWORD dwTime    // system time
)
{
//  MessageBox((((CMainFrame *)AfxGetMainWnd())->m_hWnd),"ddfaf","weixin",0);
;
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;// 获得MainFrame的指针
CTextView *pView=(CTextView *)pMain->GetActiveView();// 获得view的指针
CClientDC dc(pView);// 构造DC
  dc.TextOut(333,222,"hello world");}// 我们可以看出,使用回调函数时要获得窗口或者 APP的指针,给我们的操作带来麻烦。并不方便。

6课 菜单

1 顶层菜单默认都是Pop-up,即弹出式菜单,不能响应命令。

2.当对某菜单添加消息响应函数时, 4个类的消息响应优先次序分别是: 1.View;2.CDOC;3.CMainFrame.4.CWinAPP. 为什么?请参阅《深入浅出》

3.消息分类:
a;标准消息(以 WM_开头的消息,但不包括WM_COMMAND);从CWnd派生的类,可以接受这类消息。 
b;命令消息 ON_COMMAND(IDM_PHONE1, OnPhone1),菜单和工具栏的消息。从CCmdTarget派生的类,可以接受这类消息。  
c.通告消息:按钮,列表框发出的消息。从CCmdTarget派生的类,可以接受这类消息。  
CCmdTarget只能接受命令消息和通告消息。而从 CCmdTarget派生的CWnd 可以能接受命令消息和通告消息,也可以接受标准消息。

4.确定菜单的索引号,注意从 0开始, 分隔符也算数。
GetMenu() 获取一个菜单指针(菜单栏对象)
GetSubMenu()获取子菜单的指针
一个子菜单只能有一个缺省菜单(粗体字体显示)。//GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);
SetMunuItemBitmaps() 将指定位图与菜单项关联起来,即菜单项前面显示图形。
图形标记菜单项上显示的位图的大小是13x13
 str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));// 获得系统的菜单的位图的大小。
EnableMenuItem() 禁用菜单项
SetMenu(NULL);// 移除当前菜单 

CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();// 增加菜单,此处 detach(),如果是局部变量。

5.UPDATE_COMMAND_UI消息响应设置菜单项的状态
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{
 if(2==pCmdUI->m_nIndex)
  pCmdUI->Enable();// 当此菜单显示时,设为可用。
}

6.右键弹出菜单功能的实现方法有两个:
  a.Project->Add to Project->component and controls-> 文件夹VC components->Popup Menu OK
  b.TrackPopupMenu() 实现。
 CMenu menu;
 menu.LoadMenu(IDR_MENU1);
 CMenu *pPopup=menu.GetSubMenu(0);
 ClientToScreen(&point);//客户区坐标转换成屏幕坐标
 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,GetParent());//注意,TrackPopupMenu的参数的x,y都是屏幕坐标。

7.动态创建菜单的方法: CMenu menu;
 menu.CreatePopupMenu();
// GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
 GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
 menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
 menu.AppendMenu(MF_STRING,112,"Weixin");
 menu.AppendMenu(MF_STRING,113,"Mybole");
 menu.Detach();
 GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");
 GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,
   MF_BYCOMMAND | MF_STRING,115," 维新");
// GetMenu()->DeleteMenu(1,MF_BYPOSITION);
// GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);

8.为动态创建的菜单增加消息响应的步骤
  a.resource.h 中增加#define IDM_HELLO 123
  b.MainFrm.h 中加入afx_msg void OnHello();
  c.MainFrm.cpp 中加入ON_COMMAND(IDM_HELLO,OnHello)
  d.最后加入
void CMainFrame::OnHello()
{
 MessageBox("Hello!");
}

9.动态增加电话号码本步骤
  a.处理WM_Char 消息。如果回车,则清空字符串,窗口重绘 invalidate,将人名加入到菜单中,将字符串保存集合类 CStringArray, 用的是成员函数 Add方法。
  b.取出动态创建的菜单的数据的方法。
    1 )创建一个弹出菜单,弹出菜单下面有 4个子菜单。将子菜单的ID号连续。
    2 )在resource.h中添加 #define IDM_PHONE1 123....
    3 )添加其消息响应函数。注意注释中的文字
BEGIN_MESSAGE_MAP(CMenu2View, CView)
 //{{AFX_MSG_MAP(CMenu2View)
 ON_WM_CHAR()
 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)// 下面的4句代码原来在此处。
 //}}AFX_MSG_MAP
 // Standard printing commands
 ON_COMMAND(IDM_PHONE1, OnPhone1)// 一定要这 4句代码移到此处。
 ON_COMMAND(IDM_PHONE2, OnPhone2)
 ON_COMMAND(IDM_PHONE3, OnPhone3)
 ON_COMMAND(IDM_PHONE4, OnPhone4)
 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()
    4 )填写代码

10.如何在MainFrame 中拦截OnCommand消息?答,在它增加 OnCommand的消息处理函数即可。

11.错误调试方法:Missing ";" before "*"
 CMenu2Doc* GetDocument();// 因为CMenu2Doc是个不认识的变量,将其头文件包含进即可。

12 在CMainFrame类中重绘菜单栏 DrawMenuBar();//重绘菜单栏

0 0
原创粉丝点击