《新编C语言程序设计教程》课件第13章_第1页
《新编C语言程序设计教程》课件第13章_第2页
《新编C语言程序设计教程》课件第13章_第3页
《新编C语言程序设计教程》课件第13章_第4页
《新编C语言程序设计教程》课件第13章_第5页
已阅读5页,还剩100页未读 继续免费阅读

下载本文档

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

文档简介

第13章C到C++13.1面向对象技术13.2改进的C语言13.3C++的输入与输出13.4类与对象13.5Windows编程基础13.6程序设计举例13.1面向对象技术13.1.1面向对象技术的由来和发展

面向对象技术产生的背景与结构化程序设计方法产生的背景类似,面向对象程序设计方法(OOP)是在结构化程序设计方法的基础上发展而来的。13.1.2面向对象技术的两大要素1.对象

从概念上讲,对象代表着正在创建系统中的一个实体。从形式上讲,对象是待处理的程序单元,是数据和方法的封装体。在C++中是数据成员和成员函数的封装体。方法由若干操作构成。对象实现了信息隐藏,方法的具体实现外部是不可见的,封装的目的是阻止非法访问。对象通过消息与另一个对象传递信息。消息与方法一一对应,在C++中,消息就是成员函数的调用。2.类

类是对象的抽象及描述,是创建对象的样板,它包含着一类对象的数据描述和方法定义。一个类的所有对象都有相同的数据结构,共享相同的方法,而各个对象有各自不同的状态,类是所有对象的共同行为和不同状态的集合。由一个特定的类所创建的对象称为这个类的实例。13.1.3面向对象技术的三大机制1.封装封装的对象是数据和方法,支持数据封装就是支持数据抽象。在C++中,类是支持数据封装的工具,对象则是数据封装的实现。没有封装,就没有面向对象技术。另外,封装还提供一种对数据访问严格控制的机制。因此,数据将被隐藏在封装体中,该封装体通过操作接口与外界交换信息。

2.继承

类提供了说明一组对象结构的机制。借助于继承这一重要机制,已存在的类具有建立子类的能力,进而建立类的层次,扩充类的定义。继承提供了创建新类的一种方法,一个新类可以通过对已有类进行修改和扩充来定义。从一个类继承定义的新类,将继承已有类的方法和属性,并且可添加不包含在父类中的新方法和属性。新类被称为已有类的子类,又称为派生类,已有类称为新类的父类,又称为基类。

C++中允许单继承和多继承,一个类可以根据需要生成派生类。3.多态多态是指相同的语法结构可以代表不同类型的实体或者对不同类型的实体进行操作,即发出同样的消息被不同对象接收时导致完全不同的行为。

C++允许函数名和运算符重载,允许一个相同的标识符或运算符代表多个不同实现的函数,这是编译时的多态性。

C++中可以定义虚函数,通过定义虚函数来支持动态联编。动态联编是另一类重要的多态性,多态性形成由父类和它们的子类组成的一个树型结构。在这个树中的每一个子类可接收一个或多个具有相同名字的消息。当一个消息被这个树中一个类的一个对象接收时,这个对象动态地决定给予子类对象的消息的某种用法。这是执行时的多态性。13.1.4面向对象程序设计

面向对象的程序设计方法是目前最先进的程序设计方法。面向对象程序设计模拟人类认识问题较高、较广层次的过程。结构化程序设计强调功能抽象,程序的模块化,基于功能进行模块分解;面向对象程序设计以数据抽象为基础,综合了功能抽象和数据抽象,基于数据抽象进行模块分解。13.2改进的C语言13.2.1C++程序1.C++程序一般结构//C++程序的简单实例#include″iostream.h″main(){doublex,y;

cout<<″请输入两个数:″;

cin>>x>>y;

doublez;

z=x+y;

cout<<″x+y=″<<z<<″\n″;}C++程序的一般结构如下:①文件包含;②基类定义;③派生类定义;④成员函数定义;⑤非成员函数原型说明;⑥主函数;⑦非成员函数。2.C++程序的实现1)编辑C++源程序启动VisualC++后,出现“MicrosoftDeveloperStudio”窗口,见图13-1。该窗口有File、Edit、View、Insert、Project、Build、Tools、Window及Help9个菜单项。图13-12)编译连接和运行源程序

选择菜单项Build,出现Build的下拉式菜单,在该下拉式菜单中选择“Compile****”菜单项,这时系统开始对当前的源程序进行编译。在编译的过程中,将所发现的错误显示在屏幕下方的“Build”窗口中。所显示的出错信息指出该错误所在的行号和该错误的性质,程序员可根据这些信息采用全屏幕编辑方式修改程序。当用鼠标双击出错信息提示行时,该错误信息对应的行将加亮显示,或在该行前面用一个箭头加以指示。往往因为一个错误而出现多行出错信息,因此,常常在修改一条错误后,再重新编译,如果有错误,再继续修改,直到没有错误为止。

编译通过,显示错误信息的窗口内将显示如下信息:***.obj-0error(s),0warning(s)

编译无错误后,再进行连接。这时选择“Build”菜单中的“Build***.exe”选项。同样,对出现的错误要根据出错信息行中显示的内容进行修改,直到连接无错误为止。这时,在“Build”窗口会显示如下信息:***.exe-0error(s),0warning(s)

这说明编译、连接成功,并生成以源文件名为名字的可执行文件(.exe)。13.2.2常规改进1.新增的关键字asmcatchclassdeletefriendinlinenewoperatorprivateprotectedpublictemplatethisvirtual在将原来用C写的程序用C++编译之前,应把与上述关键字同名的标识符改名。2.注释

即用“//”导引出单行注释。当然,C中原有的/*和*/注释方法,仍可使用,并且常用于多行注释情况。3.类型转换C++支持两种不同的类型转换形式:inti=123;longl=(long)i; //C的类型转换longm=long(i); //C++的新风格

C++新风格的类型转换从形式上看像是一个函数调用,所以其可读性较好。而且,这种形式也适合于用定义函数来实现的用户定义类型的转换。

4.灵活的声明

C++程序中的变量,即对象,要求在使用之前被说明,并可以放在任何语句位置,不必非放在程序段的开始处。这样,可以随用随定义,这也是C++封装的要求。而且在远离数据项被使用处的变量声明易引起混淆或导致错误。

5.constC++中,类型限定符const用来表示常量。所以,C++中的常量是可以有类型的,程序员不必再用#define创建无类型常量。例如:

constintsize=100;

size被声明成const的变量,实际是常量,其值在程序中是用任何方法都不可修改的。

ANSIC从C++中借用了const的概念,但实现方法有所不同。

6、structC++的struct后的标识符可看作是类型名,所以定义某个struct变量比C中更加直观。例如,在C中:

structpoint{intx;inty};

structpointp;而在C++中:

structpoint{intx;inty};

pointp;union的情况也是如此。为了保持兼容性,C++仍然接受老用法。在后面会看到,C++的类就是对C中struct的扩充。7.作用域分辨运算符“∷”

“∷”是作用域分辨运算符,它用于访问在当前作用域中被隐藏的数据项。例如:

inta;

intmain()

{floata;

a=1.5;//访问当前作用域的a∷a=2;//访问全局域的a

……

}13.2.3C++的动态内存分配C程序中,动态内存分配是通过调用诸如malloc()和free()等库函数来实现,而C++给出了用new和delete运算符进行动态内存分配的新方法。voidfunc(){int*i=newint;//为指针i分配存储空间*i=10;cout<<i;deletei;//释放i指向的存储空间}以下是C++程序中用新方法实现动态内存分配的例子。用传统的C程序实现是这样的:voidfunc(void){int*i;

i=malloc(sizeof(int));*i=10;

printf(″%d″,*i);

free(i);}13.2.4引用C程序中,函数在调用时参数是通过值来传递的,这就是说函数的参数不具备返回值的能力,只有函数本身才能有返回值(用return语句)。如果需要函数的参数具有返回值的能力,往往是借用指针来实现的。C++程序通过引入引用(Reference)机制,可直接实现数据的双向传递。例如:voidswapint(int&a,int&b)//引用例{inttemp=a;

a=b;

b=temp;}

调用该函数的C++方法为:

swapint(x,y);运算符“&”表示引用,可以把参数a、b看作是调用实参的别名,C++自动把x、y的地址作为参数传递给swapint()函数,形参与实参共享存储单元。当大的结构(如用户定义类的对象)被传递给函数时,使用引用参数可使得参数传递效率得到提高。若不需改变参数的值,可用const对参数说明加以限定,从而保护数据的安全性。例如:

voidgetdata(constint&data)13.2.5C++中的函数1.main()

C并无特别规定main()函数的格式,因为通常并不关心返回何种状态给操作系统。然而,C++却要求main()函数匹配下面两种原型之一:

Voidmain()

intmain(intargc,char*argv[])2.函数原型函数原型(prototyping)的概念在前面章节已提及,其实ANSIC正是从C++中借用了这一做法。函数原型实际上就是对函数的头格式进行说明,包含函数名、参数及返回值类型。传统C中的函数说明只是定义函数的返回值的类型,并不涉及参数,如:

intsomething();而在C++中的函数说明应是详细的头格式:

intsomething(char*str,unsignedintlen);3.内联函数

当函数定义是由inline开头时,表明此函数为内联函数。编译后,它不是单独一段可调用的代码,而是被插入在对该函数的每一次调用处,从而完成与函数调用相同的功能。例如:

inlineintsum(inta,intb)

{returna+b;}

这样函数调用无需栈,代码重用。

4.缺省参数值

C++对C函数的一大重要的改进之一就是可以为函数定义缺省的参数值。看下面的程序代码:

voiddelay(intloops=1000); //函数原型,给出缺省的参数值

voiddelay(intloops) //函数定义

{for(inti=0;i<loops;i++);}

函数原型中给出了loops的缺省值,所以若调用delay函数而并没指定loops的值,程序自动取缺省值1000进行处理。例如:delay(4500);//loops值为4500delay();//loops值为1000

一个C++函数可以有多个缺省参数,并且C++要求缺省参数必须连续地放在函数参数表的尾部。当调用具有多个缺省参数时,只能由左向右匹配,并且必须是连续的。13.2.6重载1.函数的重载早期的C语言要求每个函数必须有惟一的命名,这使功能类似的函数名字不统一,通常给程序员带来不便。例如,下面是三个分别对int、long、double三种类型的数取绝对值的C函数:intabs(inti);longlabs(longl);doublefabs(doubled);

使用C++重载技术后,使用相同的函数名字,就非常直观。intabs(inti);longabs(longl);doubleabs(doubled);重载函数的特点是名字相同,参数个数和类型、返回值类型会有不同。那么,系统是如何区分使用同一个名字的重载函数的呢?实际上,相同的名字在程序编译后的内部名字是不同的,这就是C++所做的名字变异。对于重载运算符也有同样的情况。如果所调用的函数的参数类型、个数与函数说明中的某一个函数相匹配,就调用相应的函数;如果找不到相应的函数说明,就调用最便于进行类型转换的那个函数。2.运算符重载typedefstruct{doubler,i;}complex;

complexoperator+(complexc1,complexc2){complext;

t.r=c1.r+c2.r;

t.i=c1.i+c2.i;

return(t);}先看一个例子。

用operator后跟运算符号来定义重载运算符函数。重载运算符的使用,如同运算符原来的使用规则。例如:

complexx,y,sum;

sum=x+y;显然,通过重载,C++的语句更易于理解。13.3C++的输入与输出13.3.1C++流类结构1.iostream库

iostream库中具有streambuf和ios两个平行的类,这都是基本的类,分别完成不同的工作。streambuf类提供基本流操作,但不提供格式支持。类ios为格式化I/O提供基本操作。2.标准流

iostream.h说明了标准流对象cin、cout、cerr与clog。在包含iostream.h以后,这些流对象就已经自动建立并打开了。cin是标准输入流,对应于C的stdin;cout是标准输出流,对应于C的stdout;cerr和clog流被连到标准输出上对应于C的stderr。cerr和clog之间的区别是cerr没有缓冲,发送给它的任何输出立即被执行,而clog只有当缓冲区满时才有输出。缺省时,C++标准流被连到控制台上。13.3.2基本I/O操作1.输出——插入符“<<”

“<<”的左操作数为标准输出流对象,右操作数为待输出的某类型值。例如:

cout<<″Hello!\n″;//输出Hello!,并换行这时“<<”为字串或char*插入符。插入运算符返回所调用的ostream对象的引用,由于为左结合,所以可以连写。2.输入——提取符“>>”

“>>”的左操作数为标准输入流,右操作数为行输入量。它比scanf()函数更紧凑,且可读性更好,也不易出错。例如:

cin>>x;从cin输入值到x。注意它与scanf()函数不同,x前并没有地址运算符,与插入符类似,提取符“>>”也支持连写。程序员可以为自己定义的类(型)建立相应的插入和提取函数。13.3.3格式化I/O1.用ios成员函数进行格式化

在iostream.h中,有如下有关格式化标志的枚举类型定义:enum{skipws=0x0001,//跳过输入中的空白字符

left=0x0002,//输出数据左对齐

right=0x0004,//输出数据右对齐

internal=0x0008,//数据符号左对齐,数据本身右对齐

dec=0x0010,//转换基数为十进制形式oct=0x0020,//转换基数为八进制形式

hex=0x0040,//转换基数为十六进制形式

showbase=0x0080,//输出的数值数据全面带基数符号(0或0x)

showpoint=0x0100,//浮点数输出带小数点

uppercase=0x0200,//用大写字母输出十六进制数值

showpos=0x0400,//正数全面带“+”号

scientific=0x0800,//浮点数输出采用科学表示法

fixed=0x1000,//浮点数输出采用定点熟形式

unitbuf=0x2000,//完成操作后立即刷新缓冲区

stdio=0x4000//完成操作后刷新stdout,stderr};

格式标志存放于一个long整数中,要设置它可用ios的setf()函数,其一般格式为:

longsetf(longflags);该函数设置参数flags所指定的标志,返回格式更新前的标志。例如,要设置showbase标志,可使用如下语句:

stream.setf(ios∷showbase);//其中stream是所涉及的流实际上,还可以一次调用setf()来同时设置多个标志。例如:

cout.setf(ios∷showpos|ios∷scientific);清除标志可用unsetf()函数完成,其原型与setf()类似。

用flags()函数得到当前标志值和设置新标志,分别具有以下两种原型:longflags(void);longflags(longflags);除了标志外,格式输出还可设置域宽、填充字符及输出精度。其原型分别为:intwidth(intlen);charfill(charch);intprecision(intnum);请编译运行下面这段程序:#include″iostream.h″Voidmain{cout.setf(ios∷showpos|ios∷scientific);

cout<<123<<″″<<123.12<<″\n″;

cout.precision(2);

cout.width(10);

cout<<123<<″″<<123.12<<″\n″;

cout.fill(′#′);

cout.width(10);

cout<<123<<″″<<123.12;}运行结果为:+123+1.2312e+02+123 +1.23e+02######+123######### +1.23e+02

2.用操作子进行格式化流类库所定义的操作子如下:

dec、hex、oct:数值数据采用十进制或十六进制、八进制表示。

setbase(intn):设置数制转换基数为n,n为0、8、10或16,0表示使用缺省基数。ws:提取空白符。ends:插入空字符。flush:刷新与流相关联的缓冲区。resetiosflags(long):清除参数所指定的标志位。setiosflags(long):设置参数所指定的标志位。setfill(int):设置填充字符。setsprecision(int):设置浮点数输出的有效数字个数。setw(int):设置输出数据项的域宽。例如:inti=1234;cout<<setw(12)<<i<<endl;输出结果:123413.4类与对象13.4.1类的定义类的定义格式为:class类名{public:成员函数或数据成员的说明

private:成员函数或数据成员的说明};各个成员函数的实现

其中,class是定义类的关键字。类名是一标识符,通常用“T”字母开始的字符串作为类名,T用来表示类,以示与对象、函数名区别。花括号内是类的说明部分(包括前面的类头),说明该类的成员。

从访问权限上来分,类的成员可分为公有的(public)、私有的(private)和保护的(protected)三类。这里,先讨论前两类,保护的成员在继承性小节中讨论。公有的成员用public来说明。公有部分往往是一些操作(即成员函数),它是提供给用户的接口功能,这部分成员可以在程序中引用。私有的成员用private来说明。私有部分通常是一些数据成员,这些成员是用来描述该类中的对象的属性的,用户是无法访问它们的,只有成员函数或经特殊说明的函数才可以引用它们,它们是被隐藏的部分。

例如,下面给出一个关于日期的类的定义。该类是对日期抽象,该类的对象将是某一个具体日期。日期类的说明部分:

classTDate{public:

voidSetDate(inty,intm,intd);

intIsLeapYear();

voidPrint();

private:

intyear,month,day;

};

类名为TDate,该类共有6个成员。其中3个是公有成员,它们都是成员函数。SetDate()函数是设置日期的,用它来使对象获取值。IsLeapYear()函数是一个用来判断是否是闰年的函数,它的返回值为1表示该年是闰年,返回值为0表示该年不是闰年。Print()函数用来将日期输出显示。关于这三个函数的功能可通过下面的实现部分看出。还有三个私有成员,它们是int型变量year、month和day。日期类的实现部分:voidTDate∷SetDate(inty,intm,intd){year=y;month=m;day=d;}intTDate∷IsLeapYear(1){return(year%4==0&&year%100!=0)||(year%400==0);}voidTDate∷Print(){cout<<year<<"."<<month<<day<<endl;}关于日期类还可如下定义:classTDate{public:

voidSetDate(inty,intm,intd)

{year=y;month=m;day=d;}intIsLeapYear()

{return(year%4==0&&year%100!=0)||(year%400==0);}voidPrint()

{cout<<year<<″.″<<month<<″.″<<day<<endl;}private:

intyear,month,day;};

说明:

(1)在类体中不允许对所定义的数据成员进行初始化。例如,对日期类TDate类中,下面的定义是错误的:

classTDate{public:

……

private:

intyear(2000),month(2),day(18);

};

这里,不该对数据成员year、month和day进行初始化。(2)类中的数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等。也可以是对象,另一类的对象,但是自身类的对象是不可以的,而自身类的指针或引用又是可以的。当一个类的对象作为这个类的成员时,如果另一个类的定义在后,需要提前说明。

(3)一般地,在类体内先说明公有成员,它们是用户所关心的;后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。(4)经常习惯地将类定义的说明部分或者整个定义部分(包含实现部分)放到一个头文件中。后面引用起来比较方便。

(5)由于抽象数据类型的要求,C++控制了对类成员的存取,对类的私有数据成员的访问只能通过其公有的成员函数进行,因此会造成运行效率的降低。但没有关系,在不得已的情况下,C++也可以不遵循面向对象的原则,去直接访问类的私有成员,这就是友元(friend)所能做到的。一个类可以允许其它类或函数访问其私有数据,具有这种权力的其它类或函数应当显式地在该类中定义为friend(友元)。友元打破了封装。13.4.2对象的定义

对象是类的实例,对象属于某个已知的类。因此,定义对象之前,一定要先定义好该对象的类。对象在确定了它的类以后,其定义格式如下: 类名对象名表;

类名是待定的对象所属的类的名字,即所定义的对象是该类类型的对象。对象名表是用逗号分隔的对象名。对象名表中,可以是一般的对象名,还可以是指向对象的指针名或引用名,也可以是对象数组名。例如:

TDatedate1,date2,*Pdate,date[31];其中,TDate为日期类的类名,date1和date2是一般的对象名,*Pdate是指向对象的指针,date是对象数组的数组名,它有31个元素,每个元素都是一个对象。这里所说的对象都是TDate类的对象。

一个对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数,一般对象的成员表示如下:对象名.成员名或者对象名.成员名(参数表)前者用来表示数据成员,后者用来表示成员函数。例如,date1的成员可表示为:

date1.year、date1.month、date1.day分别表示TDate类的date1对象的year成员、month成员和day成员。date1.SetDate(inty,intm,intd)表示TDate类的date1对象的成员函数SeaDate()。这里,“.”是一个运算符,该运算符的功能是得到对象的成员。13.4.3构造函数和析构函数

构造函数和析构函数是在类体中说明的两种特殊的成员函数。构造函数的功能是在创建对象时,使用给定的值来将对象初始化。析构函数的功能是用来释放一个对象的。在对象删除前,用它来做一些清理工作,它与构造函数的功能正好相反。下面将重新定义前面讲过的日期类:classTDate1{public:

TDate1(inty,intm,intd);~TDate1();

voidPrint();

private:

intyear,month,day;};

TDate1∷TDate1(inty,intm,intd){year=y;month=m;day=d;

cout<<″Constructorcalled.\n″;}TDate1∷~TDate1(){cout<<″Destructorcalled.\n″;}voidTDate1∷Print(){cout<<year<<″.″<<month<<″.″<<day<<endl;}

构造函数的特点如下:

(1)构造函数是成员函数,函数体可写在类体内,也可写在类体外。

(2)构造函数是一个特殊的函数,该函数的名字与类名相同,该函数不指定类型说明,它有隐含的返回值,该值由系统内部使用。该函数可以有一个参数,也可以有多个参数。

(3)构造函数可以重载,即可以定义多个参数个数不同的函数。

(4)程序中不能直接调用构造函数,在创建对象时系统自动调用构造函数。

析构函数的特点如下:

(1)析构函数是一个特殊的成员函数,它的名字同类名,并在前面加“~”字符,用来与构造函数加以区别。析构函数不指定数据类型,并且也没有参数。

(2)一个类中只能定义一个析构函数,析构函数不能重载。

(3)析构函数可以被调用,也可以由系统调用。在下面两种情况下,析构函数会被自动调用。一是如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用;二是当一个对象是使用new运算符被动创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。

若在类定义时没有定义任何构造函数,则编译器自动生成一个不带参数的缺省构造函数,其格式如下类名∷缺省构造函数名()

{}

按构造函数的规定,缺省构造函数名同类名。缺省构造函数的这种格式也可以由程序员定义在类体中。在程序中定义一个对象而没有指明初始化,则编译器便按缺省构造函数来初始化该对象,对象的所有数据成员都初始化为零或空。

同理,如果一个类中没有定义析构函数时,则编译系统也生成一个缺省析构函数,其格式如下:类名∷~缺省析构函数名()

{}

缺省析构函数名也同类名,缺省析构函数是一个空函数。13.4.4继承性building类的说明如下所示,它用作两个派生类的基类:

classbuilding{introoms;

intfloors;

intarea;

public:

voidset-rooms(intnum);

intget-rooms();

voidset-floors(intnum);

intget-floors();

voidset-area(intnum);

intget-area();

};例如,下面是名为house的派生类,注意building是如何被继承的。//house是基类building的派生类classhouse:publicbuilding{intbedrooms;

intbaths;

public:

voidset-bedrooms(intnum);

intget-bedrooms();

voidset-baths(intnum);

intget-baths();};继承的一般形式是:class新类名:[access]基类名{//新的类体}其中,access是可选的,如果出现,它必然是public、protected或private。若缺省,则认为是私有派生private。使用public意味着基类的所有公有元素在继承它的派生类中也是公有的,保护成员能被继承。例13—1

继承性实例//程序13—1,继承性实例#include″iostream.h″classbuilding//说明一个基类building{introoms;intfloors;intarea;

public:

voidset-rooms(intnum);

intget-rooms();

voidset-floors(intnum);

intget-floors();

voidset-area(intnum);

intget-area();};classhouse:publicbuilding//说明一个由基类building

派生出的派生类house{intbedrooms;intbaths;

public:

voidset-bedrooms(intnum);

intget-bedrooms();

voidset-baths(intnum);

intget-baths();};classschool:publicbuiding//说明一个由基类building

派生出的派生类school{intclassrooms;intoffices;public:

voidset-classrooms(intnum);

intget-classrooms();

voidset-offices(intnum);

intget-offices();};voidbuilding∷set-rooms(intnum){rooms=num;}voidbuilding∷set-floors(intnum){floors=num;}voidbuilding∷set-area(intnum){area=num;}intbuilding∷get-rooms(){returnrooms;}intbuilding∷get-floors(){returnfloors;}intbuilding∷get-area(){returnarea;}voidhouse∷set-bedrooms(intnum){bedrooms=num;}voidhouse∷set-baths(intnum){baths=num;}inthouse∷get-bedrooms(){returnbedrooms;}inthouse∷get-baths(){returnbaths;}voidschool∷set-classrooms(intnum){classrooms=num;}voidschool∷set-offices(intnum){offices=num;}intschool∷get-classrooms(){returnclassrooms;}intschool∷get-offices(){returnoffices;}voidmain(){househ;schools;//定义派生类house和school下的对象h和sh.set-rooms(6);h.set-floor(8);h.set-area(4500);//对对象h进行操作h.set-bedrooms(5);h.set-baths(3);cout<<″这所房屋有″<<h.get-bedrooms();//将对象的卧室数输出cout<<″间卧室!\n″;s.set-rooms(200);s.set-classrooms(180);//对对象s进行操作s.set-offices(5);s.set-area(25000);cout<<″学校有″<<s.get-classrooms();//将对象的教室数输出coutn<<″间教室!\n″;cout<<″学校的建筑面积是″<<s.get-area();//将对象的建 筑面积输出}13.4.5运行时的多态性

1.指向派生类的指针

指向基类指针和指向派生类指针是有联系的。若有基类Bclass和Bclass派生的Dclass,则任何指向Bclass对象的指针也可用于指向Dclass对象。例如:

Bclass*p;//指向Bclass对象的指针

Bclassb;//Bclass对象

Dclassd;//由Bclass派生的Dclass对象

p=&b;//p指向Bclass对象bP=&d;//p指向Dclass对象d2.虚函数

虚函数是一个在基类中被说明为virtual,并在一个或多个派生类中被重新定义的函数。虚函数有这样的特性:当用指向基类的指针访问一个虚函数时,C++要根据该基类指针在运行时实际指向的对象类型(有可能指向某个派生类对象)来确定调用哪一个函数。所以,若指向不同的派生类对象,则执行该虚函数的不同版本。这就是所谓通过派生类和虚函数来实现的运行时的多态性。这种在运行时才确定调用哪个函数的性质常被称为动态链接(dynamicbinding)。实现运行时的多态性应注意的关键几点:(1)要用指向基类的指针。(2)虚函数要求函数原型相同,否则视为重载。(3)虚函数必须是定义类的成员,而不是友元。(4)析构函数允许是虚函数,但构造函数不可以。3.纯虚函数及抽象类

纯虚函数是在某个基类中说明的虚函数,并且它在该基类中无定义,即真正为虚拟的函数。但该基类的任何派生类都可定义该虚函数的具体实现,从而实现运行时的多态性。一个基类若含有纯虚函数作为其成员,则该基类为抽象类。抽象类的一个重要性质是不可定义抽象类的对象,它的作用就是为派生类定义公共的成员或虚函数。这不仅减少了代码的重复,而且又可实现运行时的多态性。13.5.1Windows程序

1.Windows程序特点

Windows程序以窗口为基础,采用图形用户界面和虚拟设备接口,允许动态链接,由消息驱动,通过来自操作系统的消息来处理用户输入,启动一个程序时运行WinMain()函数。13.5Windows编程基础

2.Windows编程方式

Windows编程有SDK与MFC两种编程方式。

SDK编程方式是基于Windows的API函数的类C编程方式,是一种传统的源程序代码编写方式,编写的代码运行效率高,开发难度与开发工作量大。

MFC编程方式是基于Windows的MFC类库的编程方式,是一种交互式的可视化的编程方式,编程效率高,开发难度与开发工作量相对较小。13.5.2Windows程序结构

Windows程序由WinMain()函数与窗口函数组成。

WinMain()函数可完成窗口类的注册与初始化以及窗口的建立与显示,同时建立与维护消息循环。窗口函数是消息处理函数,定义程序对接收到的不同消息的响应,是消息处理分支控制语句的集合,包含了程序对各种可能接收到的消息的处理过程。13.5.3一个简单的VC++程序例13-2画三个填充图形。//例13-2,画三个填充图形//GDI程序1.cpp:Definestheentrypointfortheapplication.#include"stdafx.h"#include<windows.h>#include<stdlib.h>#include<string.h>//函数原形LONGWINAPIWndProc(HWND,UINT,UINT,LONG);BOOLInitWindowsClass(HINSTANCE);BOOLInitWindows(HINSTANCE,int);HWNDhWndMain;//WinMain函数intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow){MSGMessage;if(!InitWindowsClass(hInstance))returnFALSE;if(!InitWindows(hInstance,nCmdShow))returnFALSE;//消息循环while(GetMessage(&Message,0,0,0)){TranslateMessage(&Message);DispatchMessage(&Message);}returnMessage.wParam;}//WinMain函数结束//消息处理函数LONGWINAPIWndProc(HWNDhWnd,UINTiMessage,UINTwParam,LONGlParam){HDChDC;HBRUSHhBrush;HPENhPen;PAINTSTRUCTPtStr;switch(iMessage){caseWM_PAINT:hDC=BeginPaint(hWnd,&PtStr);//获取HDCSetMapMode(hDC,MM_ANISOTROPIC);//设置映像模式

hPen=(HPEN)GetStockObject(BLACK_PEN);//采用黑色画笔

hBrush=(HBRUSH)GetStockObject(DKGRAY_BRUSH);//采用深灰色画刷

SelectObject(hDC,hBrush);//将指定画刷选入DCSelectObject(hDC,hPen);//将指定画笔选入DCRoundRect(hDC,50,120,100,200,15,15);//画圆角矩形

hBrush=(HBRUSH)GetStockObject(LTGRAY_BRUSH);//采用亮灰色画刷

SelectObject(hDC,hBrush);//将指定画刷选入DCEllipse(hDC,150,50,200,150);//画椭圆

hBrush=(HBRUSH)GetStockObject(HOLLOW_BRUSH);//采用虚画刷SelectObject(hDC,hBrush);//将指定画刷选入DCPie(hDC,250,50,300,100,250,50,300,50);//画饼

EndPaint(hWnd,&PtStr);//释放HDCreturn0;caseWM_DESTROY:PostQuitMessage(0);return0;default:returnDefWindowProc(hWnd,iMessage,wParam,lParam);}}//消息处理函数结束//初始化窗口函数BOOLInitWindows(HINSTANCEhInstance,intnCmdShow){HWNDhWnd;//创建窗口hWnd=CreateWindow("SIMPLEDRAWCLASS","绘图程序1",WS_OVERLAPPEDWINDOW,//窗口样式

CW_USEDEFAULT,CW_USEDEFAULT,//窗口左上角坐标为缺省值

CW_USEDEFAULT,CW_USEDEFAULT,//高和宽为缺省值

NULL,NULL,//无父窗口,无主菜单

hInstance,//应用程序当前句柄

NULL);if(!hWnd)return(FALSE);//创建不成功,返回hWndMain=hWnd;//显示窗口ShowWindow(hWnd,nCmdShow);UpdateWindow(hWnd);returnTRUE;}//初始化窗口类函数BOOLInitWindowsClass(HINSTANCEhInstance){WNDCLASSwndclass;//窗口类定义

wndclass.style=CS_HREDRAW|CS_VREDRAW;//沿水平或竖直方向改变窗口大小时,系统迫使窗口重画

wndclass.lpfnWndProc=WndProc;wndclass.cbClsExtra=0;wndclass.cbWndExtra=0;wndclass.hInstance=hInstance;wndclass.hIcon=LoadIcon(NULL,"END");wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName=NULL;wndclass.lpszClassName="SIMPLEDRAWCLASS";//窗口类注册returnRegisterClass(&wndclass);}运行结果见图13-2。图13-2例13-2程序的运行结果13.6程序设计举例

例13-3

堆栈程序。堆栈是一种先进后出的数据结构,堆栈的操作包括进栈(压入)与出栈(弹出)。程序如下://程序13-3,堆栈程序#include"iostream.h"#defineSIZE100classstack //说明一个基类stack{intstck[SIZE];

inttos;public:stack();//说明一个构造函数~stack();//说明一个析构函数voidpush(inti);//压栈函数intpop();//出栈函数};stack∷stack()//定义stack下的构造函数stack(){tos=0;cout<<"堆栈初始化!\n";}stack∷~stack()//定义stack下的析构函数~stack(){cout<<"堆栈释放!\n";}voidstack∷push(inti)//定义stack下的函数push(){if(tos==SIZE){cout<<"堆栈已满!\n";return;}stck[tos]=i;tos++;}intstack∷pop()//定义stack下的函数pop(){if(tos==0){cout<<"堆栈已空!\n";return0;}Tos--;

returnstck[tos];}main(){stacka,b;//定义stack的对象a和ba.push(1);b.push

温馨提示

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

评论

0/150

提交评论