友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第17部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
这里我们假设对该工程命名为SdkDemo1,而事实上这完全取决于你的
意愿。这个过程已经在本书的第一章中作为介绍,这里就不再重复说
明了。
2。 选择Project菜单下的Add To Project|New。。。命令,向工程中添
加一个C++ Source File (C++源文件),可以将该文件命名为
winmain。cpp,不需要键入扩展名,Microsoft Developer Studio在
创建文件时会 自动加上。cpp的后缀名。这个过程也已经在第一章中作
过介绍。阅读过该章内容的读者不应感到陌生。然后在Wordspace窗
口的FileView 中双击文件名winmain。cpp (在依赖于你在前面过程中
的设定),输入下面的源代码即可。
如果已将源代码输入为C++源文件 (以。cpp为后缀名的文件),则可以
使用Project|Add To Project|Files。。。将其添加到工程中。
图3。2 示例程序SdkDemo1的运行结果
3。 单击Build菜单下的Build SdkDemo1。exe或Build All或按下快捷
键F7 (如果未对该快捷键做过 自定义操作的话)或单击Build或Build
Minibar工具条上的 按钮,编译并创建可执行文件SdkDemo1。exe,
运行该可执行文件 (从Developer Studio中或资源管理器均可),将得
到如图3。2所示的结果。
前面已经不只一次说到过,使用这种方式编写的应用程序使用调试和
维护的难度很大。这个问题是使用直接使用SDK编程的固有总是。但
是,我们还是有办法可以使得该程序的结构更紧凑和更集中一些,从
而改善代码的可读性,也使得它更接近于使用SDK编写的真正的Win32
应用程序。
…………………………………………………………Page 142……………………………………………………………
通过分析应用程序,我们发现,在上面的程序代码中,WinMain函数
的代码显得有些过分臃肿,解决总是的办法就是将这些代码分离为单
个的函数,这样,我们就可以得到更实用的基本SDK应用程序框架,
当然,相对于MFC所提供的应用程序框架来说,我们的这个应用程序
框架几乎不值一提,但是,它的确是要比前面的示例程序好多了。
经过修改的代码如下:
#include
// 函数原型
int WINAPI WinMain(HINSTANCE;HINSTANCE;LPSTR;int);
LRESULT WINAPI WndProc(HWND;UINT;WPARAM;LPARAM);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE;int);
// WinMain 函数
int WINAPI WinMain (HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow)
{
if (!hPrevInstance)
if (!InitApplication(hInstance))
return FALSE;
if (!InitInstance(hInstance;SW_SHOW))
return FALSE;
MSG msg; // 窗口消息
// 开始消息循环
while (GetMessage(&msg;NULL;0;0))
{
…………………………………………………………Page 143……………………………………………………………
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg。wParam;
}
// WndProc 主窗口过程
LRESULT WINAPI WndProc (HWND hWnd;
UINT msg;
WPARAM wParam;
LPARAM lParam)
{
HDC hdc;
RECT rc;
HPEN hPen;hPenOld;
HBRUSH hBrush;hBrushOld;
switch (msg)
{
case WM_PAINT:
hdc=GetDC(hWnd);
GetClientRect(hWnd;&rc);
hPen=CreatePen(PS_SOLID;0;RGB(0;0;0));
hBrush=CreateHatchBrush(HS_DIAGCROSS;RGB(0;0;0));
hPenOld=SelectObject(hdc;hPen);
hBrushOld=SelectObject(hdc;hBrush);
Ellipse(hdc;rc。left;rc。top;rc。right;rc。bottom);
SelectObject(hdc;hPenOld);
…………………………………………………………Page 144……………………………………………………………
SelectObject(hdc;hBrushOld);
ReleaseDC(hWnd;hdc);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(hWnd;msg;wParam;lParam);
}
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc; // 窗口类
// 填充窗口类信息
wc。style=CS_HREDRAW|CS_VREDRAW;
wc。lpfnWndProc=WndProc;
wc。cbClsExtra=0;
wc。cbWndExtra=0;
wc。hInstance=hInstance;
wc。hIcon=LoadIcon(NULL;IDI_APPLICATION);
wc。hCursor=LoadCursor(NULL;IDC_ARROW);
wc。hbrBackground=GetStockObject(WHITE_BRUSH);
wc。lpszMenuName=NULL;
wc。lpszClassName=〃SdkDemo2〃;
// 注册窗口类
…………………………………………………………Page 145……………………………………………………………
return RegisterClass(&wc);
}
BOOL InitInstance(HINSTANCE hInstance;int nCmdShow)
{
HWND hWnd; // 主窗口句柄
// 创建应用程序主窗口
hWnd=CreateWindow (〃SdkDemo2〃; // 窗口类名
〃经过修改的第一个Win32 SDK应用程序〃; // 窗口标题
WS_OVERLAPPEDWINDOW; // 窗口样式
CW_USEDEFAULT; // 初始化 x 坐标
CW_USEDEFAULT; // 初始化 y 坐标
CW_USEDEFAULT; // 初始化窗口宽度
CW_USEDEFAULT; // 初始化窗口高度
NULL; // 父窗口句柄
NULL; // 窗口菜单句柄
hInstance; // 程序实例句柄
NULL); // 创建参数
if (!hWnd)
return FALSE;
// 显示窗口
ShowWindow(hWnd;SW_SHOW);
// 更新主窗口客户区
UpdateWindow(hWnd);
return TRUE;
}
由于上面的代码只是将前面的代码的结构作了一下调整,并没有引入
…………………………………………………………Page 146……………………………………………………………
新的API函数和其它编程内容,因此为了节省篇幅,我们这里对该代
码不再进行讲解,至于其与SdkDemo1的代码相比的优越性,由读者自
己将两段代码对比后得出。
第四节 32位编程的特点
本节假定用户是刚接触32位Windows编程的新手,那么,有必要将一
些相关的概念术语弄清楚,同时,也要把Windows 95、Windows NT和
16位的Windows 3。x相区别开来。这些最重要的概念包括进程和线程
的管理以及新的32位平坦内存模式。
在介绍32位内存管理之前,我们有必要介绍一下进程和线程这两个术
语。
进程是装入内存中正在执行的应用程序,进程包括私有的虚拟地址空
间、代码、数据及其它操作系统资源,如文件、管道以及对该进程可
见的同步对象等。进程包括了一个或多个在进程上下文内运行的线
程。
线程是操作系统分配CPU时间的基本实体。线程可以执行应用程序代
码的任何部分,包括当前正在被其它线程执行的那些。同一进程的所
有线程共享同样的虚拟地址空间、全局变量和操作系统资源。
在一个应用程序中,可以包括一个或多个进程,每个进程由一个或多
个线程构成。
线程通过 “休眠”(sleeping,暂停所有执行并等待)的方法,来做到
与进程中的其它线程所同步。在线程休眠前,必须告诉Windows,该
线程将等待某一事件的发生。当该事件发生时,Windows发给线程一
个唤醒调用,线程继续执行。也就是说,线程与事件一起被同步,除
此之外,也可以由特殊的同步对象来进行线程的同步。这些同步对象
包括:
l 互斥 不受控制的或随意的线程访问在多线程应用程序中可能会引
起很大的问题。这里所说的互斥是一小须代码,它时刻采取对共
享数据的独 占控制以执行代码。互斥常被应用于多进程的同步数
据存取。
l 信号量 信号量与互斥相似,但是互斥只允许在同一时刻一个线程
访问它的数据,而信号量允许多个线程在同一时刻访问它的数
据。Win32不知道哪一个线程拥有信号量,它只保证信号量使用的
资源量。
…………………………………………………………Page 147……………………………………………………………
l 临界区 临界区对象也和互斥相似,但它仅被属于单个进程的线程
使用。临界区对象提供非常有效的同步模式,同互斥一样,每次
在同一时间内只有一个线程可以访问临界区对象。
l 事件 事件对象用于许多实例中去通知休眠的线程所等待的事件已
经发生,事件告诉线程何时去执行某一个给定的任务,并可以使
多线程流平滑。
将所有的这些同步对象应用于控制数据访问使得线程同步成为可能,
否则,如果一个线程改变了另一个线程正在读的数据,将有可能导致
很大的麻烦。
在Win32环境下,每个运行的在进程内的线程还可以为它自己的特定
线程数据分配内存,通过Win32提供的线程本地存储 (TLS)API,应用
程序可以建立动态的特定线程数据,在运行时这些数据联系在一起。
本书将在专门的章节中讨论线程和进程的问题。
下面我们来看在32位应用程序地址空间中的内存分配和内存管理。
常见的内存分配可以划分为两类:帧分配 (frame allocation)和堆分
配 (heap allocation)。两者的主要区别在于帧分配通常和实际的内
存块打交道,而堆分配在一般情况下则使用指向内存块的指针,并
且,帧对象在超过其作用域时会被自动的删除,而程序员必须显式的
删除在堆上分配的对象。
在帧上分配内存的这种说法来源于 “堆栈帧”(stack frame)这个名
词,堆栈帧在每当函数被调用时创建,它是一块用来暂时保存函数参
数以及在函数中定义的局部变量的内存区域。帧变量通常被称作自动
变量,这是因为编译器自动为它们分配所需的内存。
帧分配有两个主要特征,首先,当我们定义一个局部变量是,编译器
将在堆栈帧上分配足够的空间来保存整个变量,对于很大的数组和其
它数据结构也是这样;其次,当超过其作用域时,帧变量将被自动的
删除。下面举一个帧分配的例子:
int Func(int Argu1;int Argu2) // 编译器将在堆栈帧上为函数参数变量分配空间
{
// 在堆栈上创建局部对象
char szDatum'256''256';
…………………………………………………………Page 148……………………………………………………………
。。。
// 超过作用域时将 自动删除在堆栈上分配的对象
}
对于局部函数变量,其作用域转变在函数退出时发生,但如果使用了
嵌套的花括号,则帧变量的作用域将有可能比函数作用域小。自动删
除这些帧变量非常之重要。对于简单的基本数据类型(如整型或字节
变量)、数组或数据结构,自动删除只是简单的回收被这些这是所占
用的内存。由于这些变量已超出其作用域,它们将再也不可以被访
问。对于C++对象,自动删除的过程要稍稍复杂一些。当一个对象被
定义为一个帧变量时,其构造函数在定义对象变量时被自动的调用,
当对象超出其作用域时,在对象所占用的内存被释放前,其析构函数
先被自动的调用。这种对构造函数和析构函数的调用看起来非常的简
便,但我们必须对它们倍加小心,尤其是对析构函数,不正确的构造
和释放对象将可能对内存管理带来严重的问题。在本书的第二章中我
们曾经历过其中的一种——指针挂起。
在帧上分配对象的最大的优越性在于这些对象将会被自动的删除,也
就是说,当你在帧上分配对象之后,不必担心它们会导致内存漏损
(memory leak)。但是,帧分配也有其不方便之处,首先,在帧上分
配的变量不可以超出其作用域,其中,帧空间往往是有限的,因此,
在很多情况下,我们更倾向于使用下面接着要讲述的堆分配来代替这
里所讲述的帧分配来为那些庞大的数据结构或对象分配内存。
堆是为程序所保留的用于内存分配的区域,它与程序代码和堆栈相隔
离。在通常情况下,C程序使用函数malloc和free来分配和释放堆内
存。调试版本 (Debug version)的MFC提供了改良版本的C++内建运算
符new和delete用于在堆内存中分配和释放对象。
使用new和delete代替malloc和free可以从类库提供的增强的内存管
理调试支持中得到好处,这在检测内存漏损时非常之有用。而当你使
用MFC的发行版本 (Release version)来创建应用程序时,MFC的发行
版本并没有使用这种改良版本的new和delete操作符,取而代之的是
一种更为有效的分配和释放内存的方法。
与帧分配不同,在堆上可分配的对象所占用的内存的总量只受限于系
统可有的所有虚拟内存空间。
以下的示例代码对比了上面讨论的内存分配方法在为数组、数据结构
和对象分配内存时的用法:
…………………………………………………………Page 149……………………………………………………………
使用帧分配为数组分配内存:
{
const int BUFF_SIZE = 128;
// 在帧上分配数组空间
char myCharArray'BUFF_SIZE';
int myIntArray'BUFF_SIZE';
// 所分配的空间在超出作用域时自动回收
}
使用堆分配为数组分配内存:
const int BUFF_SIZE = 128;
// 在堆上分配数组空间
char* myCharArray = new char'BUFF_SIZE';
int* myIntArray = new int'BUFF_SIZE';
。。。
delete '' myCharArray;
delete '' myIntArray;
使用帧分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!