c语言_游戏编程指南.doc_第1页
c语言_游戏编程指南.doc_第2页
c语言_游戏编程指南.doc_第3页
c语言_游戏编程指南.doc_第4页
c语言_游戏编程指南.doc_第5页
已阅读5页,还剩78页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

游戏编程指南彭博 著游戏编程指南目录第一章 游戏编程必需的 C+基础知识1.1 概述1.2 入门知识1.3 控制语句1.4 函数1.5 指针、数组与字符串1.6 预编译指令1.7 多文件程序的结构第二章 游戏编程必需的 C+高级知识2.1 类的定义和使用2.2 构造函数2.3 类的静态成员2.4 运算符重载2.5 类的继承2.6 虚函数与抽象类第三章 游戏编程必需的 Windows 编程知识3.1 游戏编程中基本的 Windows 程序3.2 Windows 程序中的 WinMain 函数3.3 Windows 程序中的消息处理函数第四章 DirectDraw 编程基础4.1 DirectDraw 使用前的准备工作4.2 DirectDraw 的初始化4.3 DirectDraw 对象的释放第五章 DirectDraw 编程实战5.1 调入图象5.2 透明色5.3 图象传送5.4 图象缩放5.5 后台缓存和换页5.6 页面的丢失与恢复5.7 页面的填色5.8 输出文字第六章 DirectDraw 动画制作6.1 定时器6.2 播放声音6.3 动画例子第七章 DirectDraw 编程技巧7.1 页面的锁定7.2 程序的提速7.3 排除错误的技巧7.4 C+程序优化技巧第八章 内嵌汇编8.1 内嵌汇编简介8.2 基本指令8.3 算术指令8.4 逻辑与移位指令8.5 比较、测试、转移与循环指令8.6 MMX 指令集之基本指令8.7 MMX 指令集之算术与比较指令8.8 MMX 指令集之逻辑与移位指令8.9 MMX 指令集之格式调整指令第九章 RPG 制作方法9.1 RPG 的结构9.2 RPG 制作方法9.3 常用 C+函数9.4 常用算法第十章 RPG 实例10.1 全部程序的头文件10.2 主程序10.3 HUMAN 类10.4 MAP 类10.5 BLOCK 类10.6 BULLET 类10.7 DirectDraw 相关函数10.8 初始化程序10.9 定时器程序10.10 屏幕刷新程序10.11 其他函数10.12 函数定义10.13 全局变量定义10.14 常量定义附录一、 Windows 常见消息列表 二、 Windows 常用虚拟键列表 三、 DirectDraw 函数返回值列表 四、 游戏编程常用网址内容提要本书在吸收国内外游戏编程新技术的基础上,结合本人多年游戏编程的经验和体会,不 仅由浅入深地介绍了游戏制作所必须的 C+语言、Windows 编程、DirectDraw 编程、内嵌 汇编等编程知识、而且还阐述了 DirectDraw 编程技巧与 RPG 的制作方法。为方便读者更 好地理解本书的内容和节省编程时间,本书的最后一章还提供了供读者参考的游戏实例及附 录。本书语言简明扼要,通俗易懂,内容新颖、充实,例子生动、应用性强,读者阅后能编 出自己所喜爱的游戏。本书适合于有一定编程基础的大中学生、社会上的游戏爱好者、游戏编程人员及其他有 兴趣的电脑工作者阅读。前言西方电脑游戏编程的历史可以追溯到 1972 年,那一年一个叫 Will Crowther 的人编写 了一段简单的 FORTRAN 程序,并在这个程序里设计了一张分布着陷阱的地图,游戏者必 须寻找路径避开陷阱。这个程序在后来被认为是最早的电脑游戏。自那以后,编制电脑游戏 就开始在程序员之间流行起来,不过大多数还是娱己的贵族游戏,因为当时接触计算机的还 只有科技精英们。将近三十年过去了,从“挖金子”、“波斯王子”到今天的“母巢之战”、 “黑与白”,电脑游戏在西方早已成为一种重要的娱乐,北美的游戏产业的收入已经与整个 电影产业的收入相等。相比之下,中国大陆的游戏产业起步较晚,直到 1997 年尚洋的“血狮”问世,才算真 正走上市场化的道路,可惜的是这第一次尝试的结果不尽如人意。四年已经过去了,国内的 游戏产业还尚未形成气候。造成这种状况的原因固然是多方面的,但游戏编程人员缺乏且水 平与国外有一定差距也是重要的原因之一。而这又与国内市场上缺乏游戏编程一类的书有着 密切的关联。鉴于此,本人撰写了此书。本书共分十章:第一章 游戏编程必需的 C+基础知识;第二章 游戏编程必需的 C+ 高级知识;第三章 游戏编程必需的 Windows 编程知识;第四章 DirectDraw 编程基础;第 五章 DirectDraw 编程实战;第六章 DirectDraw 动画制作;第七章 DirectDraw 编程技巧; 第八章 内嵌汇编;第九章 RPG 制作方法;第十章 RPG 实例。全书紧密围绕着游戏编程这一主题,由浅入深地介绍了游戏编程的知识、方法与技巧。 在编写过程中,作者力图遵循简明实用的原则,旨在让读者尽快掌握游戏编程,编出自己所 喜爱的游戏。本书语言简明扼要,内容充实,例子贴近游戏编程且实用性强,适合有一点编 程基础的广大有志于游戏编程的爱好者阅读。由于作者水平有限,难免有不足之处,恳请广大读者指教。第一章 游戏编程必需的 C+基础知识C 语言是公认的游戏编程首选语言。用它编制的程序不仅执行速度快,还可以充分地使 用硬件的各种资源。而 C+语言是对 C 语言的完善和升华,它的最大特点是提供了“类”, 成为了面向对象的语言。关于此,我们会在第二章详细介绍,本章将先介绍一些游戏编程所 必需的 C+基础知识。最后需要提醒大家的是,在学习本章时最好边学边实践,自己试着 写写 C+程序。1.1 概述在切入 C+语言之前,我们有必要简略地介绍一下 Visual C+的基本使用方法。首先 当然是安装 VC+,装的时候可以不选与 MFC 有关的选项。然后启动 VC+,在“文件”菜单 中选择“新建”,并在出现的窗口选择“工程”一栏中的“Win32 Console Application”,表示我们 需要编制 DOS 窗口下的程序。(如果你想编 WINDOWS 下的程序,如第三章的程序,请选 择“Win32 Application”),定好工程的名字(比如说“test”)和位置(比如说“C:test”),OK, 在弹出的对话框内选“An Empty project”,一路确定即可。现在我们已经建好了一个新工程。在屏幕的左边,我们可以看到出现了“ClassView”和“FileView”两栏,里面列出了工程中 的类和文件。接下去我们需要建立一个源文件:还是在“文件”菜单中按“新建”,出现窗口后这次要选 择“文件”栏中的“C+ Source File”,起一个名字(比如“main”),按确定即可。这时屏幕右边 出现了一片空白区域,左边的“Source File”展开后下面出现了“main.cpp”。我们可以开始编 程了!在“编辑”菜单中,有“放置可运行设置”一栏,使用它可以更改程序为 Debug 阶段或 Release 阶段。在一般情况下,建议大家选择 Release,可以减少文件大小并增加运行速度; 而在调试程序时,必须选择 Debug。在默认的情况下,编译好的程序会相应的放在工程目 录下的“Debug”或“Release”子目录内。OK,让我们看一个简单的 C+程序:/*- First C+ Program-*/#include /现在你只需知道要使用输入输出语句就必须写这行。/这一行结尾不用加分号因为它不是真正的 C+语句。/在 1.6 节将会对此做出解释。int a=5; /定义变量 a, 同时顺便赋 5 给它。C+中变量都需先定义后使用。/int 说明了 a 的数据类型,代表整数。int square(int x); /声明函数 square,参数为 int 类型,即整数。返回值也为 int 类型。/C+中的函数都需要先声明后给出定义。int square(int x) /函数真正定义。return x*x; /返回 x*x,可以在一个语句中的间隔位置插入回车将其分成几行。void main( ) /主函数,每个 DOS 下的 C+程序都需要它int b;/定义变量 b。C+中变量定义的位置是比较随意的。coutb;/输入 b, 注意箭头方向的更改。 cout“b=”bendl;/依次输出“b=”、b 的值并换行,endl 代表换行。cout“a+b=”a+b;cout“b*b=”=表示,小于等于则是=。&表示逻辑与,|表示逻辑或,!表示逻辑非。例如如果 a=8, 则( (a!=9) & ( (a=3) | (a=6) ) )为 false。(右移)非常好用,作用是把这个数的二进制形式向左或右移位(cin 和 cout中的被使用了运算符重载,具体可参阅第二章),举两个例子也许会好说明些:18(二进制形式为 0010010)3 得到 9(二进制形式为 0001001)我们可以看到,左移和右移可以代替乘或除 2 的 n 次方的作用,而且这样做可以节省 大量 CPU 运算时间。在程序优化中这一种方法是十分重要的,例如 a*9 可用(a3)+a 代替 (注意,“+”运算比“b)?a:b 可返回 a 和 b 中的较大者。值得注意的是由于 C+的操作符众多,所以运算先后次序较复杂,如果出现了出人意 料的结果请试着加几个括号。在表达式方面,C+基本与其它语言相同。只是 C+为了简化程序,提供了便利表达 式,“左值 操作符=表达式”等价于“左值= 左值 操作符 表达式”。例如 a*=6+b 等价于 a=a*(6+b),c+=8 等价于 c=c+8。1.3 控制语句C+中的控制语句格式简洁且功能强大,充分证明了它是程序员的语言。1.3.1 判断和跳转语句C+中的判断语句格式如下:if (条件) 真时执行语句; else 假时执行语句; (省去了 then)例如:if (a=9) a+; else a-;臭名昭著的跳转语句(不过有时候你还是不得不用)则是这样的: 标号:语句;(一般来说标号推荐用“_”开头)goto 标号;举个例子方便大家理解:#include void main( )int target=245; int a; cout“Guess Game”endl; couta;if (atarget)cout“a is too big!”endl;cout“Input again:”;goto _input;else if (a!=target)elsecout“a is too small!”endl;cout“Input again:”;goto _input;cout“You are right!”;1.3.2 选择语句C+中的选择语句很灵活,我们先看看与其它高级语言相似的形式:switch (变量)case 常量/常数 1:语句;/注意,可有多个语句且不需用 括起,不过其中不能定义变量。break; /为什么要加这一句呢?下面会介绍。case 常量/常数 2:语句;break;case 常量/常数 n:语句;break;default: /如所有条件都不满足则执行这里的语句。 语句;/这后面就没有必要加 break;了。break 的作用其实是防止继续执行后面的语句,试试下面的程序:#include const aaa=5;void main( )int a; cina; switch(a)case 0:coutendl“Its 0!”;case 3:coutendl“Its 3!”;case aaa:coutendl“Its aaa!”;default:coutendl“I dont know!”;按照一般人的想法,当你输入 0、2、3、5 时会分别得到“Its 0!”、 “I dont know!”、 “Its3!”、 “Its aaa!”,不过你可以试试结果是否真的是这样。试完后,你可以加上 break 再看看 结果又将是怎样。1.3.3 循环语句先介绍 while 循环语句,共有两种形式:第一种是 while (条件) 语句,意义为先判断条 件是否满足,如果满足则执行语句(否则退出循环),然后重复这个过程。第二种形式是 do 语 句 while (条件),意义为先执行语句再判断条件,如果条件成立则继续执行语句(不成立就 退出循环),这个过程也会不断重复下去。然后就是 C+最强大的 for 循环,它的形式如下:for (语句 1;条件;语句 2) 语句 3 (其中任何一部分都可省略)看上去好象很古怪,其实它就等价于这样: 语句 1;while (条件)语句 2;语句 3;比如 for (i=1;i=100;i+) coutiendl; 结果将会是输出 1 到 100 的数。又比如 for (cini; i=0; ;) cini;将会不断输入 i 至 i 大于 0 为止(省略了语句 2)。for (;1;);将会陷入死循环。在循环语句中可顺便定义变量,如 for (int i=1;i=100;i+) coutiendl;有时我们需在循环中途跳至循环外,此时 break 又可以派上用场了。有时又需要在循环 中途跳至下一次循环,continue 可以帮你这个忙。1.4 函数C+中的函数是这样定义的: 返回值数据类型 函数名(参数表)例如:语句;int Get_X_2(int x)return x*x;当返回值数据类型为 void 时表示无返回值。返回某值是通过在函数内使用 return XYZ;实现的,该语句会返回 XYZ 并立刻退出该函数。C+的函数都需在 main 函数开始前声明(除 main 函数),声明格式为:返回值数据类型 函数名(参数表); (注意有一个分号)参数表中在不引起歧义的情况下可有缺省值,例如 void xyz(int a, int b=0);,则 xyz(12)等价于 xyz(12,0)。 在函数最前加上“inline”说明其为内联函数可提高一点速度,但增大了文件的大小。 就象其它语言一样,C+中的函数可以递归调用(自己调用自己)。它还有一个很好的特点就是可以重载,例如如果在上面那个函数的基础上又定义了这样一个函数:float Get_X_2(float x)return x*x*x;那么如果 a 为一个 float 类型变量,值为 4,Get_X_2(a)会返回 64。但若 a 为 int 类型, 值也为 4,Get_X_2(a)会返回 16。有人想编一个交换 a 和 b 的函数,于是他这样写:void swap(int a, int b)int t=a;a=b;b=t;cout“a=”a“ b=”b;在函数中的输出是正常的,但运行完后 a 和 b 并没有被交换,你知道这是为什么吗? 其实原因在于:在默认状况下函数的参数都为形式参数,函数所得到的只是参数的数值而不 是参数,所以这段程序并不能真正地改变这两个变量的值。正确的解决方案是使用实参,即 在参数名前加上“&” (当你看完 1.5.1 节后你将会对此有更深的理解),就象下面的函数那 样:void swap(int &a, int &b)int t=a; a=b; b=t;下面举一个使用了函数的程序例子,这个程序的作用是计算一些几何体的体积(比较无 聊,不过挺经典):#include float pi=3.14159;float s_circle(float r);float v_cylinder(float r, float h);float v_cone(float r, float h);float v_all(float stop, float smiddle, float sbottom,float h);float v_all(float stop, float smiddle, float sbottom,float h)return (stop+4*smiddle+sbottom)*h/6;float v_cone(float r, float h)return s_circle(r)*h/3;float v_cylinder(float r, float h)return s_circle(r)*h;float s_circle(float r)return pi*r*r;void main( )float r,h;float st,sm,sb;coutCalculate some geometric forms volumeendl;coutendl0. Coneendl; cout1. Cylinderendl; cout2. Customize.choice; coutendl; switch(choice)case 0: coutr; couth;coutendlV=v_cone(r,h);break;case 1: coutr; couth;coutendlV=v_cylinder(r,h);break;case 2:coutst;coutsm;coutsb; couth;coutendlV=v_all(st,sm,sb,h);break;1.5 指针、数组与字符串1.5.1 指针指针是 C+强大功能的体现,也是一个令人又爱又恨的东西。所谓指针,顾名思义, 就是一个保存着某变量在内存中的位置的变量。指针的定义是这样的:数据类型 *指针名; 其中数据类型这一项为指针所指向的变量的类型。这里又要提到两个操作符,即&和*。&的作用是取变量的地址。而*则是&的逆运算,即 取位于该地址的变量的值。所以我们用 p=&a 即可把 p 指向 a,执行后*p 就完全等价于 a, 比如用*p=(*p)+2 可将 a 的值加上 2。需注意的是,如果没有把一个有效的地址给指针,却直接修改指针所指向的内容,可能 会出现不可预料的结果。有时程序死机就是由此引起的。指针有什么用呢?第一个用处是可以动态分配大量内存。我们知道 DOS 下很多语言对 数组的大小有很严格的限制,但 C+却可以开辟非常大的数组,而且可以用完就释放内存, 这就是指针的功劳。具体会在介绍数组时介绍。C+的下一代 C#去掉了指针,理由是它很危险,这是事实,但我觉得去掉指针还是得 不偿失的。1.5.2 数组C+中的数组和指针有着千丝万缕的联系。象其它语言一样,C+可以直接定义数组, 如 int a100;即可定义一个由 100 个 char 类型变量组成的数组;也可以在定义时顺便赋值, 例如 char b5=a, b, c, f, z;;还可以定义高维数组,如 char c20050;(相当于 BASIC 中的 c(200, 50)。使用数组时要注意几点:(1)数组的下标是从 0 开始的,上面所定义的 a 数组的下标范围为 0 到 99,刚好是 100个元素。 (2)数组越界不会有任何提示。 (3)数组需要你自己清零。如果你使用直接定义的方法产生数组,还需注意下面两点: (1)数组的大小必须是常数或常量,象 int a; int ba;这样是错误的。 (2)你得到的实际上是一个特殊的与“数组名”同名的指针。第二点也许有些费解,你可以试试这段程序就会明白:#include void main( )int abc1000; abc0=987; cout*abcendl;*abc=787;coutabc0;我们还可以直接使用指针创建数组。比如说我们要临时分配一块空间,存储 100000 个int 类型数据,那么就可以这样做:int *p; p=new int100000;(你可以将其合并为 int*p=new int100000,在这里又出现了一个新操作符“new”),则系统会在内存找到一块足够大的空闲空间,再将 p 指向这块空间的起始位置,以后就可以把 p 当成一个数组来使用了。这种办法的第一个好处是用完这块内存后可以释放内存,就象这样即可:delete p(;也是操作符);第二个好处是可以动态定义数组,例如:int a; cina; int *p=new inta;所以,建议大家多使用 new 来创建数组。“delete”不过这两种“指针”都不好用(试试 p=&p100能实现把 p 指向 p100吗?可能会死机!), 最灵活的办法是把一个另外的指针指向数组的元素,因为指针可以进行加减运算。比如说如 果 p=a0,执行 p+=46; 即可使 p 指向 a46,再执行 p-;则 p 指向 a45。看看下面的例子:#include void main( )int *p,*q;p=new int100000;q=&p0;for (int i=0;i100000;i+)*(q+)=0; /这样就可以清零,想一想!/而且比 pi=0;速度更快。q=&p1;*q=128; /把 p1变成 128 coutp1;还要讲讲使用指针创建高维数组的方法,因为此时要用到指针的指针(指针也要占内存, 也有自己的地址)!下面的一段程序演示了如何创建一个高维数组 p4060(很难懂,做好 心理准备!):int *p; /指向指针的指针!*p=new int *40; /执行完后 p 就是一个元素为指针的数组!/可以将这句与*p=new int40;对照一下。for (int i=0;i40;i+)pi=new int60; /为 p 数组中的每一指针分配内存,将其也变为一个个数组如果你弄懂了上面的程序,你就可以再玩点新花样:定义不对称数组。就象这样:int *p; *p=new int *10;for (int i=0;i10;i+)pi=new inti+1;1.5.3 字符串C+中的字符串其实也是指针的一种,因为并没有一种基本数据类型是字符串,所谓字 符串实际是一个以“/0”(这叫做转义符,代表一个 ASCII 码为 0 的符号)作为结束标志的一个 字符指针(char *),或者说是一个字符数组。用过 BASIC 的人要注意 C+中的字符串并不能 相加或相减,这些操作一般是靠使用系统提供的字符串操作函数实现的,请参阅 9.3 节。1.6 预编译指令现在该解释在第一个例子中#include 的意义了,其实这句是预编译指令。 预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。 常见的预编译指令有:(1)#include 指令该指令指示编译器将 xxx.xxx 文件的全部内容插入此处。若用括起文件则在系统的 INCLUDE 目录中寻找文件,若用“ ”括起文件则在当前目录中寻找文件。一般来说,该文件 后缀名都为“h”或“hpp”,被称为头文件,其中主要内容为对常量的定义和对函数的声明。所 以我们#include 之后编译器会看到其中对输入输出函数的声明,于是知道你要 使用这些函数,就会将包含有输入输出函数定义的库文件与编译好的你的程序连接,形成可 执行程序。(2)#define 指令 该指令有三种用法,第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与#if 配合使用;第二种是定义常数,如#define max_sprite 100,则 max_sprite 代表 100; 第三种是定义“函数”,如#define get_max(int a,int b) (ab:a?b) 则以后使用 get_max(x,y) 可得到 x 和 y 中大者(这种“函数”的本质为直接替换)。(3)#if、#else 和#endif 指令 这些指令一般这样配合使用:#if defined(标识) /如果定义了标识 要执行的指令#else要执行的指令#endif在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:#if !(defined XXX) /XXX 为一个在你的程序中唯一的标识符,/每个头文件的标识符都不应相同。/起标识符的常见方法是若头文件名为“abc.h”/则标识为“abc_h”#define XXX真正的内容,如函数声明之类#endif1.7 多文件程序的结构记得以前我第一次使用 Visual C+编游戏的时候,由于当时对 C+还不是很熟,调试 了很久都没有成功。后来把程序 E-mail 给了一位高手叫他看看问题在哪里,过了几天他把 程序送回来 时已经可以 运行了,原 来他在我的 头文件中声 明变量的语 句前都加了 一 个”extern”。这是什么意思呢?当时我还不清楚,因为很多书上并没有讲多文件的程序应该 怎么写。不过现在当你看完这一节时我想你就应该明白了。首先我们来看看多文件程序成为可执行程序的全过程:1.cpp +1.h1.obj2.cpp +3.cpp +2.h3.h2.objprogram. exe3.obj*.lib|预编译|编译|连接|我们可以发现,库文件(扩展名为 LIB,其实是一种特殊的已经编译好的程序。系统函 数的定义都是存在 LIB 内,以使你看不到它们的源代码)是在最后的连接一步加入程序的, 各个文件也是在这一步才建立联系的。extern 的作用就是告诉编译器此变量会在其它程序文件中声明。把这种外部变量声明 放在头文件里,再在每个文件中都包含这个头文件,然后只要在任何一个文件中声明变量, 所有文件就都可以使用这个变量了。如果不加 extern,各个文件使用的变量虽然同名但内容 不会统一。函数需要在各个文件中都声明之后才能使用。 下面我们就来看一个简单的例子:/*-main.h-*/#if !(defined MAIN_H)#include extern int a;void print();#define MAIN_H#endif/*-main.cpp-*/#include main.h int a;void main()a=3;print();/*-function.cpp-*/#include main.hvoid print()couta;第二章 游戏编程必需的 C+高级知识C+和 C 最大的区别在于 C+是一种面向对象(OOP)的语言。类(class)正是实现面 向对象的关键。类是一种数据类型,是对事物的一种表达和抽象。类拥有各种成员,其中有 的是数据,标识类的各种属性;有的是函数(在类中称为方法),表示对类可进行的各种操 作。举一个例子,我们可以建立一个“草”类,它可以有“高度”等各种属性和“割”、 “浇水”等 各种操作。2.1 类的定义和使用让我们先看一个使用了类的程序:/-grass.h- class grass /定义 grass 类private: /声明下面的成员为私有。类外的函数如果试图访问,编译器会告诉你发生错/误并拒绝继续编译。int height;public: /下面的成员为公有,谁都可以访问。void cut( );void water( );int get_height( );void set_height(int newh); /这个分号不要漏了!/-grass.cpp-#include #include “grass.h”/下面对类的方法进行定义void grass:cut( ) / “:”表示 cut( )是 grass 的成员。if (height=10)height-=10; /可自由访问 grass 中的任何成员。void grass:water( )height+=10;int grass:get_height( ) /因为在类的外部不能直接访问 height 所以要写这个函数。return height;void grass:set_height(int newh) /同样我们写了这个函数。if (newh=0)height=newh;void main( )grass grass1,grass2; /其实这一句和“int a,b;”没什么区别,想一想!这一句语/句被称为实例化。grass1.set_height(20); /如果你用过 VB 一定会觉得很亲切,类以外的函数即使/是访问类的公有部分也要用“.”。coutgrass1.get_height( )endl;grass1.set_height(-100); /因为 set_height 作了保护措施,所以这一句不会给/height 一个荒唐的值,这里我们可以看到类被封装的/一个好处。coutgrass1.get_height( )endl; grass1.cut( ); coutgrass1.get_height( )endl;grass2=grass1; /同一种对象可直接互相赋值。coutgrass2.get_height( )set_height(40); /由于 grass3 是指针,这里要用“-”。其实也可以/使用(*grass3).set_height(40); (“.”操作符比“*”/操作符执行时优先) ,不过这样写比较麻烦。grass3-water( );coutget_height( );看了注释你应该可以读懂这个程序,现在我们可以看到类的第一个优点了:封装性。封装指的就是象上面这样似乎故弄玄虚地把 height 隐藏起来,并写几个好象很无聊的读取和 改写 height 的函数。然而在程序中我们已经可以看到这样可以保护数据。而且在大型软件 和多人协作中,私有成员可以隐藏类的核心部分,只是通过公有的接口与其它函数沟通,当 我们修改类的数据结构时,只要再改一改接口函数,别的函数还是可以象以前一样调用类中 的数据。这样就可以使一个类作为一个模块而出现,有利于减少错误和大家的协作。有 的人也许会认为写接口函数会减慢速度,那么你可以使用内联函数类以外的函数其实也有办法直接访问类的私有部分,只要在类中声明类的方法时加入形 如”friend int XXX(int xxx, int xxx)”这样的语句,类以外的”int XXX(int xxx, int xxx)”函 数就可访问类的私有部分了。此时这个函数称为类的友元。除了 public 和 private 两种权限外还有 protected 权限,平时是和 private 一样的,后面 在讲类的继承时会进一步解释它的用途。在类的定义中要注意定义成员数据时不能同时初始化(好象 int a=0 这样),且不能用extern 说明成员数据。一种类的对象可以作为另一种类的成员。例如:class xint a;class yx b;同一种类可以互相赋值。类可作为数组的元素。可以定义指向类的指针。总之类拥有普 通的数据类型的性质。只要定义一次类,就可以大批量地通过实例化建立一批对象,且建立的对象都有直观的 属性和方法。这也是类的好处之一。结构其实是一种特殊的类,只不过类的缺省访问权限是私有,而结构的缺省访问权限是 公有。定义结构时只需把“class”换为“struct”。 一般我们在仅描述数据时使用结构,在既要 描述数据,又要描述对数据进行的操作时使用类。2.2 构造函数当将一个类实例化时,我们希望能同时将它的一些成员初始化。为此,我们可以使用构 造函数。构造函数是一个无返回值(void 都不行)且与类同名的函数。就象下面这样:#include class grasspublic:int height;grass(int height); /构造函数;grass:grass(int height)this-height=height; /对于任何一个对象的方法来说,this 永远是一个指向这个/对象的指针。所以这样写能使编译器知道是类中的 heightvoid main( )grass grass1(10); /普通对象实例化时就要给出初始化参数grass *grass2;grass2=new grass(30); /指针此时要给出初始化参数coutgrass1.height;coutheight;有时候我们需要用另一个类的属性来初始化一个类,此时我们可以使用拷贝初始化构造函数,好象这样即可:grass:grass(grass &g)height=g.height;然后我们可以这样调用它:grass grass2(grass1);来实现用 grass1 的属性初始化grass2 的属性。此时会有一个有趣的问题,grass1 初始化时要给出参数,其中有一个为一个 grass 类 的对象,但此时何来这

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论