本文摘自孙鑫<VC++深入详解3.3.1>

3.3.1  三者之间关系

很多开发人员都将窗口类、窗口类的对象和窗口之间的关系弄混淆了。为了使读者能更好地理解它们之间的关系,下面我们将模拟CWnd类的封装过程。首先新建一个Win32 Application类型的工程,取名为“WinMain”。在随后的向导窗口中选择创建一个空工程(即选择an empty project选项)。接着为该工程新建一个源文件WinMain.cpp。在该文件中,首先新建一个类CWnd,然后为其定义创建窗口函数(CreateEx)、显示窗口函数(ShowWindow)和更新窗口函数(UpdateWindow)三个函数,并定义一个成员变量(m_hWnd)。具体代码如例3-18所示。

例3-18

class CWnd

{

public:

BOOL CreateEx(DWORD dwExStyle,      // extended window style

LPCTSTR lpClassName,  // registered class name

LPCTSTR lpWindowName, // window name

DWORD dwStyle,        // window style

int x,                // horizontal position of window

int y,                // vertical position of window

int nWidth,           // window width

int nHeight,          // window height

HWND hWndParent,      // handle to parent or owner window

HMENU hMenu,          // menu handle or child identifier

HINSTANCE hInstance,  // handle to application instance

LPVOID lpParam);        // window-creation data

BOOL ShowWindow(int nCmdShow);

BOOL UpdateWindow();

public:

HWND m_hWnd;

};

小技巧:这些函数的参数可以参照MSDN中相应MFC函数的定义,然后直接复制这些参数即可。

提示:因为SDK函数数量很多,程序员记忆负担很重。MFC中使用的大部分函数名与相应的SDK函数名相同,这样做的目的就是为了方便程序员,减轻记忆负担。程序员只需要记忆两者中的一个就可以了。

接下来完成这三个函数的定义,代码如例3-19所示。

例3-19

BOOL CWnd::CreateEx(DWORD dwExStyle,      // extended window style

LPCTSTR lpClassName,  // registered class name

LPCTSTR lpWindowName, // window name

DWORD dwStyle,        // window style

int x,                // horizontal position of window

int y,                // vertical position of window

int nWidth,           // window width

int nHeight,          // window height

HWND hWndParent,      // handle to parent or owner window

HMENU hMenu,          // menu handle or child identifier

HINSTANCE hInstance,  // handle to application instance

LPVOID lpParam)        // window-creation data

{

m_hWnd=::CreateWindowEx(dwExStyle,lpClassName,dwStyle,x,y,

nWidth,nHeight,hWndParent,hMenu,hInstance,

lpParam);

if(m_hWnd!=NULL)

return TRUE;

else

return FALSE;

}

BOOL CWnd::ShowWindow(int nCmdShow)

{

return ::ShowWindow(m_hWnd,nCmdShow);

}

BOOL CWnd::UpdateWindow()

{

return ::UpdateWindow(m_hWnd);

}

其中,我们定义的CWnd类的CreateEx函数需要完成创建窗口的工作,这可以利用Win32提供的SDK函数:CreateWindowEx函数来实现。该函数返回一个句柄,标识它所创建的窗口。这里,我们就可以利用已定义的CWnd类的成员变量m_hWnd来保存这个窗口句柄。因为我们定义的CreateEx函数返回值是个BOOL型,所以应该判断一下这个窗口句柄。根据其值是否为空来决定函数是返回TRUE值,还是FALSE值。

读者应注意的是,在实际开发时,应该初始化m_hWnd变量,这可以在构造函数中实现,给它赋一个初值NULL。这里我们只是为了演示CWnd类是如何与窗口关联起来的,因此就不进行初始化工作了。

接下来定义ShowWindow函数的实现。同样,需要调用Platform SDK函数,即ShowWindow来完成窗口的显示。为了区分这两个同名函数,在调用这个Platform SDK函数时,前面加上作用域标识符(即::)。这种以“::”开始的表示方法表明该函数是一个全局函数,这里表示调用的ShowWindow函数是Platform SDK函数。因为CreateEx函数已经获取了窗口句柄并保存到m_hWnd成员变量中,所以,ShowWindow函数可以直接把这个句柄变量作为参数来使用。

提示:读者在定义自己的成员函数时,如果调用的API函数名与自己的函数名不同,那么该API函数名前可以加也可以不加“::”符号,编译器会自动识别API函数。但是如果当前定义的成员函数与内部调用的API函数名相同,那么后者前面必须加“::”符号,否则程序在编译或运行时就会出错。

我们自己定义的UpdateWindow函数的实现比较简单,直接调用SDK函数:UpdateWindow完成更新窗口的工作。

从例3-19所示代码可知,我们定义的CWnd类的后两个函数(ShowWindow和UpdateWindow)内部都需要一个窗口句柄,即需要知道对哪个窗口进行操作。

现在我们就实现了一个窗口类:CWnd。但我们知道如果要以类的方式来完成窗口的创建、显示和更新操作,那么首先还需要编写一个WinMain函数。读者并不需要记忆这个函数的写法,只要机器上有MSDN就可以了,在MSDN中找到该函数的帮助文档,直接复制其定义即可。这里,我们只是想讲解在这个函数内部所做的工作,并不是真正的实现,因此只是写出其主要的代码,如例3-20所示。

例3-20

int WINAPI WinMain(

HINSTANCE hInstance,      // handle to current instance

HINSTANCE hPrevInstance,  // handle to previous instance

LPSTR lpCmdLine,          // command line

int nCmdShow              // show state

)

{

//首先是设计窗口类,即定义一个WNDCLASS,并为相应字段赋值。

WNDCLASS wndcls;

wndcls.cbClsExtra=0;

wndcls.cbWndExtra=0;

......

//注册窗口类

RegisterClass(&wndcls);

//创建窗口

CWnd wnd;

wnd.CreateEx(...);

//显示窗口

wnd.ShowWindow(SW_SHOWNORMAL);

//更新窗口

wnd.UpdateWindow();

//接下来就是消息循环,此处省略

......

return 0;

}

请读者回想一下第1章中我们利用SDK编程时为创建窗口、显示窗口和更新窗口所编写的代码(如例3-21所示),并比较例3-20和例3-21这两段代码的区别。

例3-21

HWND hwnd;

hwnd=CreateWindowEx();

::ShowWindow(hwnd,SW_SHOWNORMAL);

::UpdateWindow(hwnd);

我们可以发现,SDK程序中多了一个HWND类型的变量hwnd。该变量用来保存由CreateWindowEx函数创建的窗口句柄,并将其作为参数传递给随后的显示窗口操作(ShowWindow函数)和更新窗口操作(UpdateWindow函数)。而我们自定义的实现代码中,CWnd类定义了一个HWND类型的成员变量:m_hWnd,用于保存这个窗口句柄。首先CWnd类的CreateEx函数创建窗口,并将该窗口句柄保存到这个成员变量,接着调用CWnd类的ShowWindow函数显示窗口时,就不需要再传递这个句柄了,因为它已经是成员变量,该函数可以直接使用它。CWnd类的UpdateWindow函数也是一样的道理。

许多程序员在进行MFC程序开发时,容易混淆一点:认为这里的CWnd类型的wnd这个C++对象所代表的就是一个窗口。因为在实践中,他们看到的现象是:当C++窗口类对象销毁时,相应的窗口也就没了。有时正好巧合,当窗口销毁时,C++窗口类对象的生命周期也到了,从而也销毁了。正因为如此,许多程序员感觉C++窗口类对象就是窗口,窗口就是这个C++窗口类对象。事实并非如此。读者可以想像一下,如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了没有呢?当然没有。当一个窗口销毁时,它会调用CWnd类的DestroyWindow函数,该函数销毁窗口后,将CWnd成员变量:m_hWnd设为NULL。

C++窗口类对象的生命周期和窗口的生命周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它们之间的纽带仅仅在于这个C++窗口类内部的成员变量:m_hWnd,该变量保存了与这个C++窗口类对象相关的那个窗口的句柄。

另一方面,当我们设计的这个C++窗口类对象销毁的时候,与之相关的窗口是应该销毁的,因为它们之间的纽带(m_hWnd)已经断了。另外,窗口也是一种资源,它也占据内存。这样,在C++窗口类对象析构时,也需要回收相关的窗口资源,即销毁这个窗口。

因此,读者一定要注意:C++窗口类对象与窗口并不是一回事,它们之间惟一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束。但C++窗口类对象销毁时,与之相关的窗口也将销毁。在我们定义的这个WinMain程序(例3-20所示代码)中,当程序运行到WinMain函数的右大括号(})时,该函数内部定义的Wnd窗口类对象的生命周期也就结束了。

这是我们自已定义的CWnd类,那么MFC提供的CWnd类是不是这样实现的呢?读者在MSDN中查看MFC提供的CWnd类,将会发现该类确实定义了一个数据成员:m_hwnd,用来保存与之相关的窗口的句柄。因为MFC中所有的窗口类都是由CWnd类派生的,于是,所有的窗口类(包括子类)内部都有这样的一个成员用来保存与之相关的窗口句柄。所以,读者不能认为我们前面创建的MFC程序Test中的CMainFrame类和CTestView类的对象就是一个窗口。

窗口类、窗口类对象与窗口 三者之间关系相关推荐

  1. 浅谈javascript中原型(prototype)、构造函数、对象实例及三者之间的关系

    转自:http://www.cnblogs.com/zhangwei412827/archive/2012/12/14/2816263.html 浅谈javascript中原型(prototype). ...

  2. 怎么安装python_零基础入门必看篇:浅析python,PyCharm,Anaconda三者之间关系

    今天为大家带来的内容是:零基础入门必看篇:浅析python ,PyCharm,Anaconda三者之间关系 众所周知,Python是一种跨平台的计算机程序设计语言,简单来说,python就是类似于C, ...

  3. java中mvc模式是什么_什么是MVC模式 MVC模式中三者之间关系

    MVC模式设计的初衷是,一旦一个项目的代码量变大的时候,src文件夹中可以写java代码,web文件夹中也可以写jsp代码.除此之外src中还可以写好多包,web文件夹中还可以写好多文件夹,现在假如新 ...

  4. 视频分辨率、帧率和码率三者之间关系详解

    帧率:FPS(每秒钟要多少帧画面):   以及Gop(表示多少秒一个I帧) 码率:编码器每秒编出的数据大小,单位是kbps,比如800kbps代表编码器每秒产生800kb(或100KB)的数据. 分辨 ...

  5. 云计算 大数据 人工智能 三者之间关系

    导读 本文作者凭借其天马行空的脑回路,用最深入浅出,清晰化的文字逻辑,讲明白了云计算.大数据和人工智能三者之间的关系.故事里面三个角色:兼具经济效益与情怀的云计算:努力把信息变为智慧的大数据:模拟人类 ...

  6. LiteCAD参考文档的学习七(图像窗口、放大窗口、鸟瞰图、对象属性窗口、极轴跟踪、跳线、事件、用户自定义命令、其它功能)

    LiteCAD API reference 一.Graphics window The LiteCAD graphics window is used to display and edit grap ...

  7. JS高级进阶总结day01---面向对象编程介绍,new的工作原理以及构造函数,原型对象,实力函数三者之间的关系

    02-面向对象编程 1.1-面向对象编程介绍 本小节知识点 1.理解什么是面向对象编程 面向对象不是一门技术,而是一种解决问题的思维方式 面向对象的本质是对面向过程的一种封装 2.理解什么是对象 对象 ...

  8. json字符串、json对象、数组 三者之间的转换

    json字符串转化成json对象 // jquery的方法 var jsonObj = $.parseJSON(jsonStr) //js 的方法 var jsonObj = JSON.parse(j ...

  9. vue将json字符串转换为数组_json字符串、json对象、数组 三者之间的转换

    var Obj = JOSN.parse("cscac"); //将JSON字符串转换成JSON对象 var "cscac" = JSON.stringify( ...

最新文章

  1. kafka streams_如何使用Kafka Streams实施更改数据捕获
  2. 简单的MapReduce项目,计算文件中单词出现的次数
  3. 对PostgreSQL中 index only scan 的初步理解
  4. openstack中手动修改虚拟机IP后不能通信
  5. concurrent(四)Condition
  6. CDH6.2 Linux离线安装
  7. 超简单的Tomcat安装过程
  8. 去掉 RHEL AS 3 内存检测达不到256MB的警告
  9. Javascript七种继承方式
  10. 使用Jstl异常:The absolute uri: http://java.sun.com/jsp/jstl/core cannotnbs
  11. struts2 使用Validation框架进行数据验证
  12. .11-Vue源码之patch(1)
  13. python输出异常信息
  14. 主板检测卡c5_主板检测卡上的05.00是什么意思帮帮忙
  15. vrchat模型保存_VRChat简易教程3-往世界里导入模型和VRC接口初探
  16. 前端开发必配置:html5shiv.js和respond.min.js的作用说明
  17. 网卡82546驱动linux,英特尔网卡驱动 Intel PRO100/1000/10GbE Win7/Win8/2
  18. 任正非:股权激励拯救了华为!
  19. php 生成条码插件,php 条形码生成插件Composer组件|php条形码code128实现方法-爱测速网...
  20. 2017 ECNA Regional Contest-G:Question of Ingestion(dp)

热门文章

  1. mysql转sqlserver_mysql转sqlserver工具
  2. html 下拉列表美化,JS+CSS实现美化的下拉列表框效果
  3. python中词云图是用来描述_python中实现词云图
  4. 钉钉产品介绍_钉钉上线安全教育新功能家校联动护航学生暑期安全
  5. java怎么设置快速修复键_Java开发环境之------MyEclipse快捷键和排除错误第一选择ctrl+1(***重点***:ctrl+1,快速修复---有点像vs中的快速using...
  6. java range(10)_Java 中的十个 ” 单行代码编程 ” ( OneLiner )
  7. datagrid 重载本地数据_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器
  8. python中string数据库_python – 将字节字符串保存到数据库中的v...
  9. 比0 冷1度c语言编程,关于DS18B20的C语言程序(精确度0.1度).doc
  10. java源程序可以有几个主类_Java源程序是由类定义组成的,每个程序可以定义若干个类,但只有一个类是主类。_学小易找答案...