您的位置:首页 > 编程语言 > C语言/C++

VC++深入详解学习记录

2015-11-11 11:34 295 查看
4000

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.创建一个完整的窗口的四个步骤 SDK,1 设计窗口类, 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

也可以在 view的OnCreate 消息响应函数中加入

     (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.VC中CString::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 ) 在 View的OnCreate 消息响应函数中: 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)

{

f745
 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();//重绘菜单栏
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: