C讲义完整版课件

上传人:公**** 文档编号:567520688 上传时间:2024-07-21 格式:PPT 页数:268 大小:496.51KB
返回 下载 相关 举报
C讲义完整版课件_第1页
第1页 / 共268页
C讲义完整版课件_第2页
第2页 / 共268页
C讲义完整版课件_第3页
第3页 / 共268页
C讲义完整版课件_第4页
第4页 / 共268页
C讲义完整版课件_第5页
第5页 / 共268页
点击查看更多>>
资源描述

《C讲义完整版课件》由会员分享,可在线阅读,更多相关《C讲义完整版课件(268页珍藏版)》请在金锄头文库上搜索。

1、C+语言编程语言编程宁 博程序设计方法的发展历程程序设计方法的发展历程n n面向过程的程序设计方法n n程序的目的:用于数学计算程序的目的:用于数学计算n n主要工作:设计求解问题的过程主要工作:设计求解问题的过程n n缺点:对于庞大、复杂的程序难以开发和维护缺点:对于庞大、复杂的程序难以开发和维护程序设计方法的发展历程程序设计方法的发展历程n n面向过程的结构化程序设计方法n n设计思路设计思路n n自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、分而治之自顶向下、分而治之n n程序结构程序结构n n按功能划分为若干个基本模块按功能划分为若干

2、个基本模块n n各模块间的关系尽可能简单,功能上相对独立;每各模块间的关系尽可能简单,功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构一模块内部均是由顺序、选择和循环三种基本结构组成组成n n其模块化实现的具体方法是使用子程序其模块化实现的具体方法是使用子程序程序设计方法的发展历程程序设计方法的发展历程n n面向过程的结构化程序设计方法n n优点优点 有效地将一个较复杂的程序系统设计任务分解有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和成许多易于控制和处理的子任务,便于开发和维护。维护。程序设计方法的发展历程程序设计方法的发展历程n n面向过程的

3、结构化程序设计方法n n缺点:可重用性差、数据安全性差、难以开发缺点:可重用性差、数据安全性差、难以开发图形界面的应用图形界面的应用n n把数据和处理数据的过程相互独立把数据和处理数据的过程相互独立n n当数据结构改变时,所有相关的处理过程都当数据结构改变时,所有相关的处理过程都要进行相应的修改要进行相应的修改n n图形用户界面的应用,很难用过程来描述和图形用户界面的应用,很难用过程来描述和实现,开发和维护都很困难实现,开发和维护都很困难面向对象的设计思想面向对象的设计思想n n面向对象的程序设计方法n n将数据及对数据的操作方法封装在一起,作为将数据及对数据的操作方法封装在一起,作为一个相互

4、依存、不可分离的整体一个相互依存、不可分离的整体对象。对象。n n对同类型对象抽象出其共性,形成类。对同类型对象抽象出其共性,形成类。n n类通过一个简单的外部接口,与外界发生关系类通过一个简单的外部接口,与外界发生关系n n基本设计思想n n封装封装n n软件复用软件复用面向对象的设计思想面向对象的设计思想n n面向对象的程序设计方法n n优点优点n n程序模块间的关系更为简单,程序模块的独程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。立性、数据的安全性就有了良好的保障。n n通过继承与多态性,可以大大提高程序的可通过继承与多态性,可以大大提高程序的可重用性,使得

5、软件的开发和维护都更为方便重用性,使得软件的开发和维护都更为方便面向对象的基本概念面向对象的基本概念 - 对象对象n n一般意义上的对象n n是现实世界中一个实际存在的事物。是现实世界中一个实际存在的事物。n n可以是有形的(比如一辆汽车),也可以是无可以是有形的(比如一辆汽车),也可以是无形的(比如一项计划)。形的(比如一项计划)。n n是构成世界的一个独立单位,具有:是构成世界的一个独立单位,具有:n n静态特征:可以用某种数据来描述静态特征:可以用某种数据来描述n n动态特征:对象所表现的行为或具有的功能动态特征:对象所表现的行为或具有的功能面向对象的基本概念面向对象的基本概念 - 对象

6、对象n n面向对象方法中的对象n n是系统用来描述客观事物的一个实体,它是用是系统用来描述客观事物的一个实体,它是用来构成系统的一个基本单位。对象由一组属性来构成系统的一个基本单位。对象由一组属性和一组行为构成。和一组行为构成。n n属性:用来描述对象静态特征的数据项。属性:用来描述对象静态特征的数据项。n n行为:用来描述对象动态特征的操作。行为:用来描述对象动态特征的操作。面向对象的基本概念面向对象的基本概念 - 类类n n分类人类通常的思维方法n n分类所依据的原则抽象n n忽略事物的非本质特征,只注意那些与当前目忽略事物的非本质特征,只注意那些与当前目标有关的本质特征,从而找出事物的共

7、性,把标有关的本质特征,从而找出事物的共性,把具有共同性质的事物划分为一类,得出一个抽具有共同性质的事物划分为一类,得出一个抽象的概念。象的概念。n n例如,石头、树木、汽车、房屋等都是人们在例如,石头、树木、汽车、房屋等都是人们在长期的生产和生活实践中抽象出的概念。长期的生产和生活实践中抽象出的概念。面向对象的基本概念面向对象的基本概念 - 类类n n面向对象方法中的类n n具有相同属性和行为的一组对象的集合具有相同属性和行为的一组对象的集合n n为属于该类的全部对象提供了抽象的描述,包为属于该类的全部对象提供了抽象的描述,包括属性和行为两个主要部分。括属性和行为两个主要部分。n n类与对象

8、的关系:类与对象的关系:犹如模具与铸件之间的关系,一个属于某类的犹如模具与铸件之间的关系,一个属于某类的对象称为该类的一个实例。对象称为该类的一个实例。面向对象的基本概念面向对象的基本概念 - 封装封装n n把对象的属性和行为结合成一个独立的系统单位n n尽可能隐蔽对象的内部细节。对外形成一个边界(或者说一道屏障),只保留有限的对外接口使之与外部发生联系。面向对象的基本概念面向对象的基本概念 - 继承继承n n继承对于软件复用有着重要意义,是面向对象技术能够提高软件开发效率的重要原因之一。n n定义:特殊类的对象拥有其一般类的全部属性与行为,称作特殊类对一般类的继承。n n例如:将Person

9、作为一个一般类,Student便是一个特殊类。面向对象的基本概念面向对象的基本概念 - 多态多态性性n n多态性是指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个特殊类中具有不同的语义。n n例如:数的加法数的加法-实数的加法实数的加法 -复数的加法复数的加法C+语言概述语言概述n nC+语言的产生n nC+的特点n n一个简单的C+程序实例C+语言的产生语言的产生n nC+是从C语言发展演变而来的,首先是一个更好的C。n n引入了类的机制,最初的C+被称为带类的C。n n1983年正式取名为C+。C+语言的标准化

10、工作从1989年开始,于1994年制定了ANSI C+标准草案。以后又经过不断完善,成为目前的C+。C+的特点的特点n n全面兼容Cn n它保持了它保持了C C的简洁、高效和接近汇编语言等特的简洁、高效和接近汇编语言等特点点n n对对C C的类型系统进行了扩充的类型系统进行了扩充n nC+C+也支持面向过程的程序设计,不是一个纯也支持面向过程的程序设计,不是一个纯正的面向对象的语言正的面向对象的语言n n支持面向对象的方法n n类、对象、继承、抽象、封装、类、对象、继承、抽象、封装、.一个简单的一个简单的C+程序实例程序实例#include void main(void) coutHello!

11、n; coutWelcome to c+!n;一个简单的一个简单的C+程序实例程序实例运行结果:Hello!Welcome to c+!注释注释n nC中的注释:块注释 /* */n n/* This is a comment */* This is a comment */n n不允许嵌套不允许嵌套n nC+中的注释n nC+C+继承了继承了C C的块注释方式的块注释方式n n增加了一种行注释方式增加了一种行注释方式n nC+C+把任何一行中从把任何一行中从“ “/”/”开始直到该行结束开始直到该行结束的所有内容皆视为注释的所有内容皆视为注释注释注释 - 一个简单的例子一个简单的例子#inc

12、lude#include #include#include voidmain()voidmain()/CheckingifakeywordisESC/CheckingifakeywordisESCintinti,key;i,key;while(1)while(1)key=key=getchgetch();();/Getakeyfromconsole/Getakeyfromconsoleif(key=if(key=x1Bx1B printf(printf(“nEscapenEscape! !”); );return;return;elseelseprintf(printf(“nKeycodenK

13、eycodeis%2XHis%2XH”,key);,key); 作用域作用域n n模块:在C语言中模块的概念是指在花括号之间的一组语句n n作用域的种类n n局部模块作用语:变量定义在模块范围内局部模块作用语:变量定义在模块范围内n n文件作用域:变量定义在全局范围内,只限于文件作用域:变量定义在全局范围内,只限于当前文件的存取;当前文件的存取;n n全局作用域:变量定义在全局范围内,对全程全局作用域:变量定义在全局范围内,对全程序有效。实现方式:序有效。实现方式:includeinclude和和externexternn n类作用域:变量定义在类中,在类范围内有效类作用域:变量定义在类中,在

14、类范围内有效作用域作用域: : 运算符: 指明作用域。 例 int x; void f() int x=1; :x=2; return; 作用域作用域 - 一个简单的例子一个简单的例子#include int global = 10;void main() int global = 5; printf(“The value of global is : %dn”, global); return;作用域作用域 - 作用域分辨操作符作用域分辨操作符n n全局变量访问全局变量访问n n:global:global#include #include intint global = 10; globa

15、l = 10;void main() void main() intint global = 5; global = 5; printf(“Theprintf(“The value of inner global is : % value of inner global is : %dndn”, global);”, global); printf(“Theprintf(“The value of outer global is : % value of outer global is : %dndn”, ”, :global);:global); return; return; 指针指针n

16、n指针是C语言的一个非常重要的特征n n实际上是内存地址,非常灵活且非常高效n n但又潜伏着非常大的危险性n n具有数据类型,可以进行指针运算n n无值型指针,void *,是一种非常有用且十分灵活的指针类型常量指针常量指针n n定义格式 const type * ptr_name; n n其含义是指向常量的指针n n不允许通过指针来修改其指向的对象的值不允许通过指针来修改其指向的对象的值n n可以修改指针的值可以修改指针的值n n例子const const intint * * ptrptr; ; const const intint i=10; i=10;ptrptr = & i; / =

17、 & i; / 修改指针的值修改指针的值* *ptrptr = i+10; / = i+10; / 错误操作,试图修改指针指向的内容错误操作,试图修改指针指向的内容常量指针常量指针n n例子const char * const char * ptrptr; ;const char str10=“const char str10=“hehehehehehe”; ”;ptrptr = = strstr; / ; / 指针赋值指针赋值ptr3 = ; / ptr3 = ; / 错误操作,试图修改指针指向的内容错误操作,试图修改指针指向的内容常量指针常量指针const const intint * *

18、 ptrptr; ;intint i=10; i=10;ptrptr = &i; / = &i; / 指针赋值指针赋值, , 等价于等价于(const (const intint *)&i *)&i* *ptrptr = 15; / = 15; / 错误操作,试图修改指针指向的内容错误操作,试图修改指针指向的内容i+; i+; coutcout “i = ” * “i = ” *ptrptr endlendl; ;输出结果为:输出结果为:1111常量指针常量指针n n不允许将const类型的变量地址赋值给一般的变量指针intint * * ptrptr; ;const const intint

19、 i = 10; i = 10;ptrptr = &i; / = &i; / 编译错误编译错误ptrptr = ( = (intint *)&i; / *)&i; / 正确正确* *ptrptr = * = *ptrptr + 1; + 1; coutcout “i = ” * “i = ” *ptrptr endlendl; ;输出结果为:输出结果为:1111指针常量指针常量n n定义格式 type * const ptr_name; n n其含义是指针变量的值不可改变n n不允许修改指针变量的地址值不允许修改指针变量的地址值n n可以修改指针指向的变量值,如果指向的对象可以修改指针指向的变

20、量值,如果指向的对象不是一个常量的话不是一个常量的话n n例子n nintint * const ptr1; * const ptr1;n nvoid * const ptr2;void * const ptr2;指针常量指针常量n nintint num=10; num=10;n nintint * const * const const_ptrconst_ptr = # = #n nconst const intint * * ptr_to_constptr_to_const = # = #n nintint const * const * ptrptr =

21、 # = #n nconst_ptrconst_ptr =( =(intint * const) & num; / * const) & num; / 编译错误编译错误n n / /试图修改一个指针常量的地址值试图修改一个指针常量的地址值n n* *ptrptr = num; / = num; / 编译错误编译错误n n / /试图修改常量指针指向的对象值试图修改常量指针指向的对象值voidvoid类型类型1.1.说明函数没有返回值;说明函数没有返回值; void void fun(voidfun(void) ) return ; return ;2.2.表示函数不需要任何入口

22、参数;表示函数不需要任何入口参数; double double fun(voidfun(void) ) 3.3.可将指针说明为可将指针说明为voidvoid型。这种指针可被赋以其它任何类型型。这种指针可被赋以其它任何类型的指针。的指针。 double a=9.0;double a=9.0; double *pd=&a; double *pd=&a; void *p; void *p; p=pd; p=pd; 练习练习用指针写一个swap函数,交换两个整数 a和b的值。打印交换前后a,b的值。引用引用n n先看一个简单的例子intint i, &j=i; i, &j=i;i=1;i=1;cout

23、cout“i=” i“i=” iendlendl; / output: i=1; / output: i=1j+;j+;coutcout“i=” i“i=” iendlendl; / output: i=2; / output: i=2i+;i+;coutcout“j=” j“j=” jendlendl; / output: j=3; / output: j=3引用引用n n引用的定义格式n ntype & type & ref_nameref_name = = var_namevar_name; ;n n引用定义说明n n引用必须在定义时进行初始化引用必须在定义时进行初始化n n被引用的变量

24、必须在引用定义之前定义被引用的变量必须在引用定义之前定义n n引用一经定义,便无法重新引用其它变量引用一经定义,便无法重新引用其它变量引用引用n n对引用概念的理解n nintint i=10, &j=i; i=10, &j=i;n n在物理存储中,变量在物理存储中,变量i i有存储单元,但引用有存储单元,但引用j j没没有存储单元,其具体表现是变量有存储单元,其具体表现是变量i i和引用和引用j j的物的物理地址是相同的理地址是相同的n n内存单元内存单元 0XEFFF21 0XEFFF21 有两个名字,可以将引用有两个名字,可以将引用j j理解为变量理解为变量i i的别名的别名n n在同一

25、个模块中的引用没有太大意义,但在函在同一个模块中的引用没有太大意义,但在函数调用时可以实现传名调用数调用时可以实现传名调用10变量i引用j0XEFFF21练习练习用引用写一个swap函数,交换两个整数 a和b的值。打印交换前后a,b的值。C+的内存格局的内存格局n n全局数据区(data area)n n代码区(code area)n n栈区(stack area)n n堆区(heap area)C+的内存格局的内存格局全局变量、静态数据、常量存放在全局数据区;所有类成员函数和非成员函数代码存放在代码区;为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区;其余的空间都被称为堆

26、区。堆内存的分配与释放当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。在C+中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成,其使用的格式如下:指针变量名=new 类型名(初始化式);delete 指针名;new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。 堆内存的分配与释放

27、C中的malloc和free函数在C+中可以用alloc.h头文件中声明void * malloc(size_t size); void free(void *);堆内存的分配与释放if (array=(if (array=(intint*)*)malloc(arraysizemalloc(arraysize* *sizeof(intsizeof(int)=NULL)=NULL) coutcout“cant allocate more memory.n”;“cant allocate more memory.n”;exit(1);exit(1); free(arrayfree(array);

28、);堆内存的分配与释放newnew 类型名T(初值列表)功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依据初值列表调用合适的构造函数。结果值:成功:T类型的指针,指向新分配的内存。失败:0(NULL)堆内存的分配与释放n ndeletedelete 指针P功能:释放指针P所指向的内存。P必须是new操作的返回值。堆内存的分配与释放用初始化式用初始化式(initializer)来显式初始化来显式初始化 例如:例如:int *pi=new int(0);当当pi生命周期结束时,必须释放生命周期结束时,必须释放pi所指向的目标:所指向的目标: delete pi;注意这时释放了注意这时释

29、放了pi所指的目标的内存空间,也就是撤销了该目标,所指的目标的内存空间,也就是撤销了该目标,称动态内存释放(称动态内存释放(dynamic memory deallocation),),但指针但指针pi本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。 堆堆i用初始化式用初始化式(initializer)来显式初始化来显式初始化 例如:例如:int *pi=new int(0);当当pi生命周期结束时,必须释放生命周期结束时,必须释放pi所指向的目标:所指向的目标: delete pi;注意这时释放了注意这时释放了pi所指的

30、目标的内存空间,也就是撤销了该目标,所指的目标的内存空间,也就是撤销了该目标,称动态内存释放(称动态内存释放(dynamic memory deallocation),),但指针但指针pi本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。 堆内存的分配与释放堆堆i堆内存的分配与释放对于数组进行动态分配的格式为:对于数组进行动态分配的格式为:指针变量名指针变量名=new =new 类型名类型名 下标表达式下标表达式;delete delete 指向该数组的指针变量名指向该数组的指针变量名; ;堆内存的分配与释放if (arra

31、y=new int arraysize)=NULL) coutcout“cant allocate more memory.n”;“cant allocate more memory.n”;exit(1);exit(1); delete array; 堆内存的分配与释放【例】动态数组的建立与撤销#include #include void main()int n;char *pc;cout请输入动态数组的元素个数n;pc=new charn; /strcpy(pc,堆内存的动态分配);coutpcendl;delete pc; / 撤销并释放pc所指向的n个字符的内存空间return ;堆内存

32、的分配与释放动态分配的三个特点:首先,变量动态分配的三个特点:首先,变量n在编译时没有在编译时没有确定的值,而是在运行中输入,按运行时所需分配堆空确定的值,而是在运行中输入,按运行时所需分配堆空间,这一点是动态分配的优点,可克服数组间,这一点是动态分配的优点,可克服数组“大开小用大开小用”的弊端。的弊端。delete pc是将是将n个字符的空间释放,而用个字符的空间释放,而用delete pc则只释放了一个字符的空间;则只释放了一个字符的空间;其次如果有一个其次如果有一个char *pc1,令,令pc1=p,同样可用同样可用delete pc1来释放该空间。尽管来释放该空间。尽管C+不对数组作

33、边不对数组作边界检查,但在堆空间分配时,对数组分配空间大小是纪界检查,但在堆空间分配时,对数组分配空间大小是纪录在案的。录在案的。第三,没有初始化式(第三,没有初始化式(initializer),),不可对数组初始不可对数组初始化。化。 堆内存的分配与释放指针使用的几个问题:指针使用的几个问题:动态分配失败。动态分配失败。返回一个空指针(返回一个空指针(NULL),),表示表示发生了异常,堆资源不足,分配失败。发生了异常,堆资源不足,分配失败。指针删除与堆空间释放。删除一个指针指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了实际意思是删除了p所指的目标(变量或对象等)

34、,释放了它所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除本身,释放堆空间后,成了所占的堆空间,而不是删除本身,释放堆空间后,成了空悬指针空悬指针 堆内存的分配与释放内存泄漏(内存泄漏(memory leak)和重复释放和重复释放。new与与delete 是配对使用的,是配对使用的, delete只能释放堆空间。只能释放堆空间。如果如果new返回的指针值丢失,则所分配的堆空间无返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险法回收,称内存泄漏,同一空间重复释放也是危险的,所以必须妥善保存的,所以必须妥善保存new返回的指针,以保证不返回的指针,以保证

35、不发生内存泄漏,也必须保证不会重复释放堆内存空发生内存泄漏,也必须保证不会重复释放堆内存空间。间。动态分配的变量或对象的生命期。动态分配的变量或对象的生命期。无名对象,它的无名对象,它的生命期并不依赖于建立它的作用域,比如在函数中生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。我们也称建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(堆空间为自由空间(free store)就是这个原因。就是这个原因。 练习练习建立动态整型数组用户输入数组长度,并且用户输入初始化数组,求和并打印到屏幕。练习练习编写程序。测试堆内存的容量:每次申请一个数组,内含100个

36、整数,直到分配失败,并打印堆容量。面向对象编成的基本特点面向对象编成的基本特点n n抽象n n封装n n继承n n多态性面向对象编成的基本特点面向对象编成的基本特点 -抽抽象象n n抽象是对具体对象(问题)进行概括,抽出这一抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。类对象的公共性质并加以描述的过程。n n只注意问题的本质及描述,而忽略实现过程或只注意问题的本质及描述,而忽略实现过程或细节。细节。n n数据抽象:描述某类对象的属性或状态(对象数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)。相互区别的物理量)。n n代码抽象:描述某类对象的共有的行为特

37、征或代码抽象:描述某类对象的共有的行为特征或具有的功能。具有的功能。n n抽象的实现:通过类定义中的抽象的实现:通过类定义中的publicpublic来实现。来实现。n n抽象出来的数据和行为对外是可见的抽象出来的数据和行为对外是可见的面向对象编成的基本特面向对象编成的基本特 - 抽抽象象class Watch public: void SetTime(int NewH, int NewM, int NewS); void ShowTime(); private: int Hour,Minute,Second;抽象的行为抽象的行为面向对象编成的基本特点面向对象编成的基本特点 -封封装装n n将

38、实现细节涉及到的数据成员、实现代码封装在类中,对外是不可见的。n n目的是曾强安全性和简化编程,使用者不必了目的是曾强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员以特定的访问权限,来使用类的成员n n实现封装:通过类定义中的实现封装:通过类定义中的privateprivate来实现来实现面向对象编成的基本特点面向对象编成的基本特点 -封封装装class Watch public: void SetTime(int NewH, int NewM, int NewS); void ShowTime()

39、; private: int Hour,Minute,Second;封装的数据成员封装的数据成员面向对象编成的基本特点面向对象编成的基本特点 -继继承承n n继承是继承是C+C+中支持层次分类的一种机制,允许程中支持层次分类的一种机制,允许程序员在保持原有类特性的基础上,进行更具体的序员在保持原有类特性的基础上,进行更具体的类定义,形成一个类层次关系类定义,形成一个类层次关系n n继承的主要目的和作用继承的主要目的和作用n n代码复用代码复用n n提高程序设计语言的建模能力提高程序设计语言的建模能力n n接近现实世界的实际模型,也就是模型表达能接近现实世界的实际模型,也就是模型表达能加自然,这

40、样使得建模现实世界更加容易加自然,这样使得建模现实世界更加容易n n具体的内容将在后面的章节中介绍具体的内容将在后面的章节中介绍面向对象编成的基本特点面向对象编成的基本特点 -多态性多态性n n多态的概念:同一名称,不同的功能实现方式。多态的概念:同一名称,不同的功能实现方式。n n目的:达到行为标识统一,减少程序中标识符的目的:达到行为标识统一,减少程序中标识符的个数。个数。类的概念和定义类的概念和定义n n类的概念n n类是用来描述一组对象的相同属性和行为,它类是用来描述一组对象的相同属性和行为,它为属于该类的全部对象提供了统一的抽象描述为属于该类的全部对象提供了统一的抽象描述n n包括抽

41、象属性和抽象行为包括抽象属性和抽象行为n n利用类可以实现细节进行封装利用类可以实现细节进行封装n n包括用于实现的成员数据和成员函数包括用于实现的成员数据和成员函数n n利用类易于编写大型复杂程序,其模块化程度利用类易于编写大型复杂程序,其模块化程度比比C C中采用函数更高中采用函数更高类的概念和定义类的概念和定义n n类的定义class class 类名称类名称 public:public: 公有成员公有成员(外部接口)(外部接口) private:private: 私有成员私有成员 ( (实现细节实现细节) ) protected: protected: 保护型成员保护型成员 ( (用于

42、类层次的访问控制用于类层次的访问控制) ) n n类是一种用户自定义的类型类的概念和定义类的概念和定义class Watchclass Watch public: public: void void SetTimeSetTime(int(int NewHNewH, , intint NewMNewM, , intint NewSNewS); ); void void ShowTimeShowTime();(); private: private: intint HourHour, , MinuteMinute, , SecondSecond; ; ;成员数据成员函数类的概念和定义类的概念和定义

43、void Watch : void Watch : SetTime(intSetTime(int NewHNewH, , intint NewMNewM, , intint NewSNewS) ) Hour= Hour=NewHNewH; ; Minute= Minute=NewMNewM; ; Second= Second=NewSNewS; ; void Watch : ShowTime()void Watch : ShowTime() coutcoutHour:Minute:Second;Hour:Minute:Second; 类的概念和定义类的概念和定义n n成员数据n n与一般的变量

44、定义相同,但需要将它放在类的与一般的变量定义相同,但需要将它放在类的定义体中定义体中n n成员函数n n在类中说明原形,在类外定义函数体实现,并在类中说明原形,在类外定义函数体实现,并在函数名前使用类名加以限定。也可以直接在在函数名前使用类名加以限定。也可以直接在类中定义函数体,形成内联成员函数。类中定义函数体,形成内联成员函数。n n在类的定义外部实现内联成员函数时一定要显在类的定义外部实现内联成员函数时一定要显式加上式加上inlineinline关键词关键词n n允许定义重载函数和带缺省形参值的函数允许定义重载函数和带缺省形参值的函数类的概念和定义类的概念和定义n n内联成员函数n n为了

45、提高运行时的效率,对于较简单的函数可为了提高运行时的效率,对于较简单的函数可以声明为内联形式。以声明为内联形式。n n实现内联成员函数的方式:n n将函数体放在类的定义中。将函数体放在类的定义中。n n在外部实现中显式使用在外部实现中显式使用inlineinline关键字。关键字。类的概念和定义类的概念和定义n n内联成员函数的内部实现内联成员函数的内部实现class Locationclass Location public: public: void void Init(intInit(int initXinitX, , intint initYinitY) ) X= X=initXini

46、tX; ; Y= Y=initYinitY; ; intint GetXGetX() return X;() return X; intint GetYGetY() return Y;() return Y; private: private: intint X,Y; X,Y;内联成员函数类的概念和定义类的概念和定义n n内联成员函数的外部实现class Locationclass Location public: public: void void Init(intInit(int initXinitX, , intint initYinitY);); intint GetXGetX();(

47、); intint GetYGetY();(); private: private: intint X,Y; X,Y;类的概念和定义类的概念和定义n n内联成员函数的外部实现inlineinline void Location: void Location: Init(intInit(int initX,intinitX,int initYinitY) ) X= X=initXinitX; ; Y= Y=initYinitY; ; inlineinline intint Location:GetXLocation:GetX() () return X; return X; inlineinli

48、ne intint Location:GetYLocation:GetY() () return Y; return Y; 类的概念和定义类的概念和定义n n私有成员 (私有数据成员和私有成员函数)n n在关键字在关键字privateprivate后面声明,后面声明,只允许本类中的函只允许本类中的函数访问,而类外部的任何函数都不能访问。数访问,而类外部的任何函数都不能访问。n n如果如果紧跟在类名称的后面声明私有成员,则紧跟在类名称的后面声明私有成员,则关关键字键字privateprivate可以可以省略。省略。类的概念和定义类的概念和定义n n私有成员class Watchclass Wat

49、ch privateprivate: : intint Hour,Minute,SecondHour,Minute,Second; ; public: public: void void SetTime(intSetTime(int NewHNewH, , intint NewMNewM, , intint NewSNewS); ); void ShowTime(); void ShowTime(); ;private可以缺省可以缺省类的概念和定义类的概念和定义n n私有成员class Watch w;class Watch w;intint i; i;i=i=w.Hourw.Hour; /

50、; / 编译错误,试图访问私有成员数据编译错误,试图访问私有成员数据w.SetTimew.SetTime(); / (); / 正确,因为正确,因为SetTimeSetTime是公有成是公有成员函数员函数类的概念和定义类的概念和定义n n公有成员(公有数据成员和公有成员函数)n n在关键字在关键字publicpublic后面声明,它们是类与外部的接后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和口,任何外部函数都可以访问公有类型数据和函数。函数。类的概念和定义类的概念和定义n n公有成员class Watchclass Watch publicpublic: : intin

51、t Hour,Minute,SecondHour,Minute,Second; ; void void SetTime(intSetTime(int NewHNewH, , intint NewMNewM, , intint NewSNewS); ); void ShowTime(); void ShowTime(); ;类的概念和定义类的概念和定义n n公有成员class Watch w;class Watch w;intint i; i;i=i=w.Hourw.Hour; /; /正确,因为正确,因为HourHour是公有成员数据是公有成员数据w.SetTimew.SetTime(); /

52、 (); / 正确,因为正确,因为SetTimeSetTime是公有成是公有成员函数员函数类的概念和定义类的概念和定义n n保护成员(保护数据成员和保护成员函数)n n与与privateprivate类似,在类的外部函数中不能访问类类似,在类的外部函数中不能访问类中的保护成员中的保护成员n n在类的子类中可能可以访问该类的保护成员,在类的子类中可能可以访问该类的保护成员,这取决于访问控制,在后面讲述这取决于访问控制,在后面讲述类的概念和定义类的概念和定义n n不能试图给类定义中的数据成员进行初始化不能试图给类定义中的数据成员进行初始化n n类仅仅是类型的说明,而对象的数据成员的初始类仅仅是类型

53、的说明,而对象的数据成员的初始化必须有构造函数来完成化必须有构造函数来完成class Watch private: int Hour = 0, Minute = 0, Second = 0; public: void SetTime(int NewH, int NewM, int NewS); void ShowTime();错误!C+中的结构中的结构n nC+中的结构比C语言中的结构具有更加强大的功能n n在C+中结构和类具有类似的功能n n在结构中可以定义成员函数、可以进行继承;在结构中可以定义成员函数、可以进行继承;可以象可以象C+C+的类一样实现;的类一样实现;n n所有的解释与类相同

54、,除了所有的解释与类相同,除了n n只是缺省的访问控制是只是缺省的访问控制是publicpublic,而类的缺省访问控制,而类的缺省访问控制是是privateprivaten n结构是结构是C+C+扩展扩展C C的第一步,但不推荐这种结构的第一步,但不推荐这种结构编程方式编程方式C+中的结构中的结构StructStruct Person Person intint age; age; intint getAgegetAge() return age; ;() return age; ; ;StructStruct Student : Person Student : Person intint

55、 snosno; ; intint getSnogetSno() return () return snosno; ; ; 对象对象n n类的对象是该类的某一特定实体,即类类型的变量。n n定义形式: 类名 对象名;n n例: watch myWatch;n n当然也可以有对象的指针,对象的引用等对象对象n n类中成员的访问方式n n类中的成员函数访问类中的成员数据类中的成员函数访问类中的成员数据n n直接使用成员名直接使用成员名n n类外访问类外访问n n使用使用“ “对象名对象名. .成员名成员名” ”方式访问方式访问 publicpublic 属性的成员属性的成员对象对象n n类中成员的

56、访问方式#include#include class Watchclass Watch ./ ./类的定义略类的定义略类的定义略类的定义略 /./.类的实现略类的实现略类的实现略类的实现略void void main(voidmain(void) ) Watch Watch myWatchmyWatch; ; myWatch.SetTimemyWatch.SetTime(8,30,30);(8,30,30); myWatch.ShowTimemyWatch.ShowTime();(); 构造函数构造函数n n构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态

57、。n n在对象创建时由系统自动调用。n n如果程序中未定义出,则系统自动产生出一个缺省形式的构造函数 - 缺省构造函数n n允许为内联构造函数、重载构造函数、带缺省形参值的构造函数构造函数构造函数n n定义说明n n构造函数名与所在的类名相同;构造函数名与所在的类名相同;n n构造函数没有返回类型,因此在定义构造函数构造函数没有返回类型,因此在定义构造函数时不能带有任何带有返回值的返回语句;时不能带有任何带有返回值的返回语句;n n不能将构造函数说明为不能将构造函数说明为voidvoid类型;类型;n n可以有返回语句可以有返回语句 return;return;n n构造函数只能用于构造类的实

58、例对象,它的调构造函数只能用于构造类的实例对象,它的调用是隐含的,不能对任何已经构造的对象再次用是隐含的,不能对任何已经构造的对象再次进行构造函数的显式调用;进行构造函数的显式调用;构造函数构造函数n n定义说明n n构造函数一般总是被定义为公有成员,否则在构造函数一般总是被定义为公有成员,否则在外部定义类对象时无法对其进行初始化外部定义类对象时无法对其进行初始化n n构造函数的定义与普通成员函数一样,可以在构造函数的定义与普通成员函数一样,可以在类定义体内部,也可以放在类定义体的外部,类定义体内部,也可以放在类定义体的外部,且可以直接访问其类所在的所有其他成员,包且可以直接访问其类所在的所有

59、其他成员,包括成员数据和成员函数括成员数据和成员函数构造函数构造函数n n定义说明定义说明class Person class Person intint age; age;Person() Person() age=0;age=0; ; ;class Student :public Person class Student :public Person intint stnostno; ; ; Person p; / Person p; / 编译错误编译错误必须将该构造函数变为公有成员构造函数构造函数n n一个类可以有多个构造函数,和普通函数一样,同名的构造函数相互重载Class Class

60、 d_Stringd_String Public:Public: d_Stringd_String(); (); d_String(constd_String(const d_Stringd_String *); *); d_String(constd_String(const char *); char *); ; ;构造函数构造函数n n对象的初始化d_Stringd_String str1; / str1; / 调用缺省构造函数调用缺省构造函数d_Stringd_String str1(); / str1(); / 调用缺省构造函数调用缺省构造函数d_Stringd_String str

61、1(“Bing Wang”); / str1(“Bing Wang”); / 调用构造函数调用构造函数d_Stringd_String str1= str1=d_Stringd_String(); / (); / 调用缺省构造函数调用缺省构造函数d_Stringd_String str1= str1=d_String(“Bingd_String(“Bing Wang”); Wang”); / / 调用构造函数调用构造函数构造函数构造函数n n构造函数一般用来对对象的成员数据进行初始化构造函数一般用来对对象的成员数据进行初始化n n当类的成员数据比较多时这种初始化语句比较多当类的成员数据比较多时

62、这种初始化语句比较多Class Account Class Account public:public: Account() Account() _name = 0; _name = 0; _balance = 0.0; _balance = 0.0; _ _acct_nmbracct_nmbr = 0; = 0; ; ;构造函数构造函数 Account(constAccount(const char * name, double balance, char * name, double balance, intint acct_nmbracct_nmbr) ) _name = new cha

63、r strlen(name)+1 ; _name = new char strlen(name)+1 ; strcpy(_namestrcpy(_name, name);, name); _balance = balance; _balance = balance; _ _acct_nmbracct_nmbr = = acct_nmbracct_nmbr; ; ; ;.; ;构造函数构造函数n n成员初始化表成员初始化表Class Account Class Account public:public: Account() : _name ( 0), _balance (0.0), Accou

64、nt() : _name ( 0), _balance (0.0), _ _acct_nmbracct_nmbr (0) ; (0) ; Account(constAccount(const char * name, double balance, char * name, double balance, intint acct_nmbracct_nmbr) : ) : balance = balance = balancebalance, _, _acct_nmbracct_nmbr = = acct_nmbracct_nmbr; ; _name = new char strlen(name

65、)+1 ; _name = new char strlen(name)+1 ; strcpy(_namestrcpy(_name, name);, name); ; ;.;n n初始化列表的效率比赋值语句的效率要高初始化列表的效率比赋值语句的效率要高成员初始化表构造函数构造函数n nconst成员函数n n一个类中的成员函数可以是一个类中的成员函数可以是constconstclass class d_Setd_Set public: public: d_Setd_Set();(); unsigned long cardinality() const; unsigned long cardina

66、lity() const; d_Booleand_Boolean is_emptyis_empty() const;() const; const成员函数成员函数n nconst成员函数含义是该函数只能读取该类中的成员数据,而不能修改,因此n n该成员函数不能修改所在类的成员数据该成员函数不能修改所在类的成员数据n n该成员函数不能调用所在类中的非该成员函数不能调用所在类中的非constconst成员函成员函数数n n根据const成员函数的定义n n不能将构造函数定义为不能将构造函数定义为constconst类型,因为它要初类型,因为它要初始化成员数据的值始化成员数据的值n nconst关键

67、字可以被用于参与对重载函数的区分n nconst是函数类型的一个组成部分,因此在实现部分也要带const关键字。const对象对象n nconst对象与其他类型的const对象一样n nconst对象调用构造函数初始化对象后不能再被修改 const const d_Stringd_String str1 = “Bing Wang”; str1 = “Bing Wang”; . . str1 = “Bing Wang”; / str1 = “Bing Wang”; /错误错误 n n通常,const对象只能调用const成员函数 举例举例#includeclass R public: R(int

68、 r1, int r2)R1=r1;R2=r2; void print(); void print() const; private: int R1,R2;举例举例void R:print() coutR1:R2endl;void R:print() const coutR1;R2endl;void main() R a(5,4); a.print(); /调用调用void print() const R b(20,52); b.print(); /调用调用void print() const隐含的隐含的this指针指针n n来看一个例子来看一个例子Class Class ArrayXArra

69、yX Private : Private : intint dim; dim; double * x; double * x;Public : Public : ArrayX(intArrayX(int d); d); ArrayXArrayX();(); void void WriteXWriteX( ( intint n, double n, double valval); ); double double ReadX(intReadX(int n); n); void void PrintXPrintX( ( intint n ); n ); void void PrintAllPrin

70、tAll( void );( void ); ;隐含的隐含的this指针指针n n问题:成员函数,例如ReadX,是如何知道当前对象的地址的呢?因为只有通过地址才能操作该对象中的数据成员;这正好是this指针的作用n n为了解决上述问题,实际上在成员函数参数表中存在一个额外的参数:n nWriteX(WriteX(ArrayArray * this * this, double , double valval); );n nReadX(ReadX(ArrayXArrayX * this * this, , intint n); n);n nPrintX(PrintX(ArrayXArrayX

71、* this * this, , intint n); n);n nPrintAll(PrintAll(ArrayXArrayX * this * this); );隐含的隐含的this指针指针n n这个ArrayX * this指针是隐含的,由他的调用这提供,指向被操作的对象n n在成员函数内部也可以使用this指针,例如Void Void Write(intWrite(int n, double n, double valval) ) if (a if (a dim)-dim) thisthis-xnxn = = valval; ; n n上述程序中this指针可缺省Void Write(

72、int n, double val) if (a dim) xn = val;隐含的隐含的this指针指针n nThis指针除了上述用法之外,可以使用在返回值中class Screen Public: Screen(); Screen & set( char ); Screen & move( int, int ); Screen & clear(); Screen & display();Private: ./ 数据成员说明; 隐含的隐含的this指针指针n nThis指针除了上述用法之外,可以使用在返回值中Screen & Screen:clear( char bg) _cursor =

73、0; _screen.assign(_screen.size(), bg); return *this;n nmyScreen.clear().move(2,2).set(*).display();缺省构造函数缺省构造函数n n缺省构造函数是指没有参数的构造函数缺省构造函数是指没有参数的构造函数n n当类的定义中没有定义构造函数时,系统会为该当类的定义中没有定义构造函数时,系统会为该类生成一个缺省构造函数,在进行对象说明时系类生成一个缺省构造函数,在进行对象说明时系统会调用系统生成的缺省构造函数统会调用系统生成的缺省构造函数n n系统生成的缺省构造函数不做任何事情系统生成的缺省构造函数不做任何

74、事情n n当类中用户定义了构造函数后,编译器就会忽略当类中用户定义了构造函数后,编译器就会忽略系统生成的缺省构造函数,即使你没有定义缺省系统生成的缺省构造函数,即使你没有定义缺省构造函数构造函数n n建议用户定义自己的缺省构造函数,因为系统生建议用户定义自己的缺省构造函数,因为系统生成的缺省构造函数不做任何事情成的缺省构造函数不做任何事情缺省构造函数缺省构造函数Class Class d_Stringd_String Public:Public: d_Stringd_String(); / (); / 缺省构造函数缺省构造函数 d_String(constd_String(const d_St

75、ringd_String *); *); d_String(constd_String(const char *); char *); ; ;d_Stringd_String strstr; / ; / 调用用户定义的缺省构造函数调用用户定义的缺省构造函数缺省构造函数缺省构造函数Class Class d_Stringd_String Public:Public: / /d_Stringd_String(); / (); / 缺省构造函数缺省构造函数 / /d_String(constd_String(const d_Stringd_String *); *); / /d_String(con

76、std_String(const char *); char *); / / 没有其它构造函数定义没有其它构造函数定义; ;d_Stringd_String strstr; / ; / 调用系统生成的缺省构造函数调用系统生成的缺省构造函数缺省构造函数缺省构造函数Class Class d_Stringd_String Public:Public: / /d_Stringd_String(); / (); / 缺省构造函数缺省构造函数 d_String(constd_String(const d_Stringd_String *); *); d_String(constd_String(cons

77、t char *); char *); ; ;d_Stringd_String strstr; / ; / 编译错误,因为编译器忽略系编译错误,因为编译器忽略系 / / 统生成的缺省构造函数统生成的缺省构造函数拷贝构造函数拷贝构造函数n n拷贝构造函数是一种特殊的构造函数,其形参为本类的对象引用class class 类名类名 public : public : 类名(形参);类名(形参);/ /构造函数构造函数 类名(类名类名(类名 &对象名);对象名);/ /拷贝构造函数拷贝构造函数 . . ;类名类名: : 类名(类名类名(类名 &对象名)对象名)/ /拷贝构造函数的实拷贝构造函数的实现

78、现 函数体函数体 拷贝构造函数拷贝构造函数class Locationclass Location public: public: Location(intLocation(int xx=0,int xx=0,int yyyy=0)X=xx; Y=0)X=xx; Y=yyyy; ; Location(LocationLocation(Location & p); p); intint GetXGetX() return X;() return X; intint GetYGetY() return Y;() return Y; private: private: intint X,Y; X,Y

79、; ;拷贝构造函数拷贝构造函数n n如果没有定义类的拷贝构造函数,系统就会自动生成一个默认的拷贝构造函数,其功能是把初始值对象的每个数据成员的值都复制到新建立的对象中n n何时调用拷贝构造函数Location L1;Location L1;Location L2(L1); / Location L2(L1); / 调用拷贝构造函数调用拷贝构造函数Location L3 = L1; / Location L3 = L1; / 调用拷贝构造函数调用拷贝构造函数F( L1 ); / F( L1 ); / 调用拷贝构造函数调用拷贝构造函数L1 = g(); /L1 = g(); /调用拷贝构造函数调用

80、拷贝构造函数, , 为被返回为被返回 / / 的对象建立一个临时对象的对象建立一个临时对象拷贝构造函数拷贝构造函数n n课堂提问既然系统能够自动生成一个默认的拷贝构造函既然系统能够自动生成一个默认的拷贝构造函数,那么用户是否还有必要生成自己的拷贝构数,那么用户是否还有必要生成自己的拷贝构造函数呢?理由是什么?造函数呢?理由是什么?浅拷贝与深拷贝浅拷贝与深拷贝n n浅拷贝 - 通过一个例子来理解浅拷贝的概念C语言的例子char * p1, *p2;p1 = malloc( 20 );strcpy(p1, “Bin Wang”);p2 = p1;Bin Wang指针p1指针p2浅拷贝与深拷贝浅拷贝

81、与深拷贝n n浅拷贝 - 通过一个例子来理解浅拷贝的概念C+的例子Class String String(); String(const char *) String();private: char * str;String:String() str = 0; String:String() if (str!=0) delete str; String:String(const char * s) str = new char strlen(s)+1; strcpy(str, s); String * s1 = new String(“Bin Wang”); /String * s2 = ne

82、w String (s1); / 调用缺省拷贝构造函数浅拷贝与深拷贝浅拷贝与深拷贝n n浅拷贝浅拷贝 - - 通过一个例子来理解浅拷贝的概念通过一个例子来理解浅拷贝的概念n n浅拷贝的缺点浅拷贝的缺点当一个指针指向同一个对象或内存单元时存在当一个指针指向同一个对象或内存单元时存在潜在的错误源泉,例如潜在的错误源泉,例如当对象当对象s1s1被析构以后,对象被析构以后,对象s2s2无法析构是由于无法析构是由于缺省拷贝构造函数引起的,必须重写缺省拷贝构造函数引起的,必须重写strstrBin Wangs1s2浅拷贝与深拷贝浅拷贝与深拷贝n n深拷贝Class String String(); Str

83、ing(const char *); String(String &); String();private: char * str;String:String() str = 0; String:String() if (str!=0) delete str; String:String(const char * s) str = new char strlen(s)+1; strcpy(str, s); String:String(String & s) str = new char strlen(s.str)+1; strcpy(str, s.str); String * s1 = new

84、 String( “Bin Wang”); String * s2 = new String( s1 ); 浅拷贝与深拷贝浅拷贝与深拷贝n n深拷贝n n深拷贝可以很好地避免指针悬挂问题strstrBin Wangs1s2Bin Wang析构函数析构函数n n完成对象被删除前的一些清理工作。完成对象被删除前的一些清理工作。n n在对象的生存期结束的时刻系统自动调用它,然在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。后再释放此对象所属的空间。n n如果程序中未定义析构函数,编译器将自动产生如果程序中未定义析构函数,编译器将自动产生一个缺省的析构函数。一个缺省的析构函数。n

85、n与构造函数一样,析构函数一般总是被说明为类与构造函数一样,析构函数一般总是被说明为类的一个公有函数成员,由的一个公有函数成员,由 加上函数名称构成,没加上函数名称构成,没有返回值,且没有任何参数有返回值,且没有任何参数析构函数析构函数n n当一个对象中没有涉及动态内存分配时,可以使当一个对象中没有涉及动态内存分配时,可以使用系统生成的缺省析构函数。如果涉及到动态内用系统生成的缺省析构函数。如果涉及到动态内存问题时,应该编写自己的显式析构函数;否则存问题时,应该编写自己的显式析构函数;否则存在内存泄漏问题存在内存泄漏问题n n当然也可能存在需要处理的情况:在对象的生命当然也可能存在需要处理的情

86、况:在对象的生命周期内动态获得的各种资源,在析构函数中应该周期内动态获得的各种资源,在析构函数中应该需要处理;例如该对象获得的互斥锁,在析构函需要处理;例如该对象获得的互斥锁,在析构函数中应该释放该锁;数中应该释放该锁;析构函数析构函数Class Account Class Account Public: Account();Public: Account(); Account ( const char *, double = 0.0 ); Account ( const char *, double = 0.0 ); Account ( const Account & ); Account

87、( const Account & ); Account(); Account();Private: char *_name;Private: char *_name; unsigned unsigned intint _ _acct_nmbracct_nmbr; ; double _balance; double _balance; ;inlineAccount:account() delete _name;例例1(1) (1) Account global( “James Joyce” );Account global( “James Joyce” );(2) (2) intint mai

88、n() main() (3) Account local ( Anna (3) Account local ( Anna LiviaLivia PlurabellePlurabelle”, 10000 );”, 10000 );(4) Account & (4) Account & loc_refloc_ref = global; = global;(5) Account * pact = 0;(5) Account * pact = 0;(6) Account (6) Account local_twolocal_two ( “ ( “stephenstephen hero” ); hero

89、” );(7) pact = new Account( “Stephen (7) pact = new Account( “Stephen DedalusDedalus” );” );(8) (8) (9) delete pact;(9) delete pact;(10) (10) 例例1n n该例子中调用了4次构造函数;n n对应地,程序也要调用4次析构函数;类的应用举例类的应用举例n n一圆型游泳池如图所示,现在需在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。游泳池过道类的

90、应用举例类的应用举例#include #include const float PI = 3.14159;const float PI = 3.14159;const float const float FencePriceFencePrice = 35.0; = 35.0;const float const float ConcretePriceConcretePrice = 20.0; = 20.0;/定义类定义类定义类定义类Circle Circle 及其数据和方法及其数据和方法及其数据和方法及其数据和方法class Circleclass Circle private: private

91、: float radius; float radius; public: public: Circle(floatCircle(float r); / r); /构造函数构造函数构造函数构造函数 float Circumference() const; /float Circumference() const; /圆周长圆周长圆周长圆周长 float Area() const; /float Area() const; /圆面积圆面积圆面积圆面积;类的应用举例类的应用举例/ / 类的实现类的实现类的实现类的实现/ / 构造函数初始化数据成员构造函数初始化数据成员构造函数初始化数据成员构造函数

92、初始化数据成员radiusradiusCircle:Circle(floatCircle:Circle(float r): r): radius(rradius(r) ) / / 计算圆的周长计算圆的周长计算圆的周长计算圆的周长float float Circle:CircumferenceCircle:Circumference() const() const return 2 * PI * radius; return 2 * PI * radius; / / 计算圆的面积计算圆的面积计算圆的面积计算圆的面积 float float Circle:AreaCircle:Area() con

93、st() const return PI * radius * return PI * radius * radiusradius; ; 类的应用举例类的应用举例void main ()void main () float radius; float radius; float float FenceCostFenceCost, , ConcreteCostConcreteCost; ; / / 提示用户输入半径提示用户输入半径提示用户输入半径提示用户输入半径 coutcout Enter the radius of the pool: ; radius; radius; / / 定义定义定义

94、定义 Circle Circle 对象对象对象对象 Circle Circle Pool(radiusPool(radius);); Circle Circle PoolRim(radiusPoolRim(radius + 3); + 3);类的应用举例类的应用举例/ / 计算栅栏造价并输出计算栅栏造价并输出计算栅栏造价并输出计算栅栏造价并输出 FenceCostFenceCost = = PoolRim.CircumferencePoolRim.Circumference() * () * FencePriceFencePrice; ; coutcout Fencing Cost is $

95、Fencing Cost is $ FenceCostFenceCost endlendl; ; / / 计算过道造价并输出计算过道造价并输出计算过道造价并输出计算过道造价并输出 ConcreteCostConcreteCost = ( = (PoolRim.AreaPoolRim.Area() - () - Pool.AreaPool.Area()*()*ConcretePriceConcretePrice; ; coutcout Concrete Cost is $ Concrete Cost is $ ConcreteCostConcreteCost endlendl; ; 类的应用举例

96、类的应用举例运行结果运行结果Enter the radius of the pool: 40Fencing Cost is $945.598Concrete Cost is $391.119课堂练习课堂练习用类实现计数器抽象数据类型用类实现计数器抽象数据类型class class CCounterCCounter public: public: CCounterCCounter();(); void Up(); void Up(); void Down(); void Down(); void Zero(); void Zero(); void Value(); void Value(); p

97、rivate: private: intint a; a; ; ;课堂练习课堂练习设计一个栈类型,考虑下面的数据结构设计一个栈类型,考虑下面的数据结构const const intint STACK_SIZE=20; STACK_SIZE=20;class class CStackCStack public: public:CStackCStack();();CStack(intCStack(int szsz); ); CStackCStack();();void void push(intpush(int e); e);intint pop(); pop();private:private:

98、intint *item; *item;intint sp; sp;intint size; size;课堂练习课堂练习private:private:intint *item; *item;intint sp; sp;intint size; size; ;CPoint类类声明一个声明一个CPointCPoint类来描述点对象。类来描述点对象。 / /point.hpoint.h class class CPointCPoint public: public:CPointCPoint ( (intint x, x, intint y); y);intint XCoordXCoord();()

99、; intint YCoordYCoord();(); void void Move(intMove(int xOffsetxOffset, , intint yOffsetyOffset); ); private: private: intint X,Y; X,Y; ; CPointCPoint: : TPoint(intTPoint(int x,intx,int y) y) X=x; X=x; Y=y; Y=y; intint CPointCPoint: : XCoordXCoord() return X;() return X; intint CPointCPoint: : YCoord

100、YCoord() return Y; () return Y; void void CPointCPoint: : Move(intMove(int xOffsetxOffset, , intint yOffsetyOffset) ) X+= X+= xOffsetxOffset; ;Y+=Y+=yOffsetyOffset; ; void void main(voidmain(void) ) CPointCPoint p(3,4); p(3,4); p.Move(10,20); p.Move(10,20); coutcout(p.XCoordp.XCoord(), (), p.XCoordp

101、.XCoord()()endlendl; ; 类的静态成员类的静态成员n n在C语言中有静态变量的概念n n局部静态变量局部静态变量 - - ,相当于一个局部范围内能,相当于一个局部范围内能够使用的全局变量够使用的全局变量n n对应地,在C+中也有静态成员的概念n n静态数据成员静态数据成员n n静态成员函数静态成员函数静态数据成员静态数据成员n n用关键字static声明n n该类的所有对象维护该成员的同一个拷贝Class Score Class Score char student20; char student20; intint language; language; intint m

102、athsmaths; ; intint history; history;Public: static int passmark; static int passnum; Score(); Score();静态数据成员静态数据成员n n必须在类外定义和初始化,用必须在类外定义和初始化,用(:)(:)来指明所属的类来指明所属的类intint Score:passmarkScore:passmark = 60; = 60;IntInt Score:passnumScore:passnum = 0; = 0;n n上述定义和初始化必须放在上述定义和初始化必须放在main()main()函数的外面来进

103、行,象函数的外面来进行,象全局变量一样全局变量一样int Score:passmark = 60;Int Score:passnum = 0;void main() . Void main() int Score:passmark = 60; int Score:passnum = 0;Static int Score:passmark = 60;Static int Score:passnum = 0;void main() . 静态数据成员静态数据成员n n静态数据成员在类中仅仅是说明静态数据成员在类中仅仅是说明n n其内存空间是在初始化语句时进行分配的其内存空间是在初始化语句时进行分配的

104、n n在类中说明静态数据成员是不能进行初始化在类中说明静态数据成员是不能进行初始化Class Score char student20; int language; int maths; int history;Public: static int passmark = 60; static int passnum = 0; Score(); Score();静态数据成员静态数据成员n n静态数据成员可以是公有的,也可以是私有的或静态数据成员可以是公有的,也可以是私有的或保护的保护的n n公有静态数据成员可以在类外访问公有静态数据成员可以在类外访问n n象普通成员函数一样通过对象来访问象普通成

105、员函数一样通过对象来访问void main() cout Score:passmark endl;.void main() Score s;cout s.passmark endl;没有任何理由推荐这种调用方式,因为静态数据成员是独立于类对象的静态数据成员静态数据成员n n如果在类的成员函数中访问静态数据成员时可以如果在类的成员函数中访问静态数据成员时可以将类名和分辨符省略将类名和分辨符省略Class Score char student20; int language; int maths; int history;Public: static int passmark; static in

106、t passnum;Score() Score:passmark=60; passnum=0; Score();静态数据成员静态数据成员n n例子:给予上面的类说明#include int Score:passmark = 50;int Score:passnum = 0;void main() cout Score:passmark endl; / 输出是多少? Score s; cout s.passmark endl; / 输出又是多少? cout Score:passmark endl; / 输出又是多少? 静态数据成员静态数据成员n n私有静态数据成员n n象公有静态成员一样进行定义

107、和初始化象公有静态成员一样进行定义和初始化n n在类外不能进行访问,只有类的成员函数才能在类外不能进行访问,只有类的成员函数才能访问它访问它n n保护型静态数据成员n n象公有静态数据成员一样定义和初始化象公有静态数据成员一样定义和初始化n n在类中可以访问它在类中可以访问它n n在类的子类中也可能可以访问它在类的子类中也可能可以访问它 - - 在后面类在后面类的继承中讲述的继承中讲述静态成员函数静态成员函数n n一个成员函数可以是静态的class Student char filename20; int age; int grade; static int total;public: voi

108、d PrintInfo(); static void PrintTotal();静态成员函数静态成员函数静态成员函数n n与静态数据成员类似,静态成员函数不依赖于类的对象而存在n n类外代码可以使用类名和作用域操作符来调用静态成员函数。n nclass_name:func_name(arguments_listclass_name:func_name(arguments_list); );n nStudent:PrintTotalStudent:PrintTotal();();n n静态成员函数也可以象普通成员函数那样调用n nStudent s;Student s;n ns.PrintTot

109、als.PrintTotal();();没有任何理由推荐这种调用方式,因为静态成员函数是独立于类对象的静态成员函数静态成员函数n n在类的成员函数中调用静态成员函数时,其类名在类的成员函数中调用静态成员函数时,其类名和分别符可以省略和分别符可以省略n nStudent:PrintTotalStudent:PrintTotal(); / (); / 推荐方式推荐方式n nPrintTotalPrintTotal(); / (); / 非推荐方式非推荐方式n n静态成员函数也有三种类型静态成员函数也有三种类型n n公有静态成员函数公有静态成员函数n n私有静态成员函数私有静态成员函数n n保护型静

110、态成员函数保护型静态成员函数n n公有静态成员函数可以在类外进行调用公有静态成员函数可以在类外进行调用n n私有静态成员函数可以在类内进行调用私有静态成员函数可以在类内进行调用n n保护型静态成员函数可以在类内或其子类中调用保护型静态成员函数可以在类内或其子类中调用静态成员函数静态成员函数n n类的静态成员函数不能试图直接访问普通数据成员和调用普通成员函数class A public: static void f1();private: int x;void A:f1() cout x endl; /错误 A a; cout a.x endl; / 正确 为什么?静态对象静态对象n n局部静态

111、对象与C中的静态局部变量非常类似,但需要注意构造函数和析构函数的调用n n其构造函数在代码执行过程中,第一次遇到它其构造函数在代码执行过程中,第一次遇到它的变量定义时被调用,当函数再次进入时就不的变量定义时被调用,当函数再次进入时就不再再次调用构造函数再再次调用构造函数n n其析构函数在整个程序执行结束后,进程退出其析构函数在整个程序执行结束后,进程退出前被调用前被调用静态对象静态对象n n全局静态对象(变量)与全局对象(变量)等价n n全局静态对象与C中的全局静态变量也非常类似,但也需要注意构造函数和析构函数的调用n n其构造函数在其构造函数在main()main()函数前被调用,如果有多函

112、数前被调用,如果有多个全局对象,构造函数调用顺序取决于变量的个全局对象,构造函数调用顺序取决于变量的定义顺序定义顺序n n其析构函数在整个程序执行结束后,进程退出其析构函数在整个程序执行结束后,进程退出前被调用前被调用练习练习 记录一个类在程序运行时有多少个对象被创建。记录一个类在程序运行时有多少个对象被创建。class class TPointTPoint public:public:TPoint(intTPoint(int x,intx,int y); y);intint XCoordXCoord();();intint YCoordYCoord();(); TPointTPoint();

113、();static static static static intintintint ObjectExistedObjectExistedObjectExistedObjectExisted();();();();private:private:static static static static intintintint Number; Number; Number; Number;intint X,Y; X,Y; ; 友元友元n n通常,对于类的私有成员,外部类或外部函数是通常,对于类的私有成员,外部类或外部函数是无法访问和调用它们的无法访问和调用它们的n n但有的时候某些类或函数需要

114、访问另外一些类的但有的时候某些类或函数需要访问另外一些类的私有成员私有成员n n通过友元可以实现这种访问控制通过友元可以实现这种访问控制n n友元函数友元函数n n友元类友元类n n需要注意的是,友元打破了数据封装和数据隐藏需要注意的是,友元打破了数据封装和数据隐藏的概念。但这种开放在某些时候是非常必要的,的概念。但这种开放在某些时候是非常必要的,尤其是对于操作符的重载尤其是对于操作符的重载友元函数友元函数n n友元函数是在类定义中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员n n访问对象中的成员必须通过对象名友元函数友

115、元函数#include #include class Point public: Point(double xi, double yi) X=xi; Y=yi; double GetX() return X; double GetY() return Y; friend double Distance( Point& a, Point& b); private: double X, Y;友元函数友元函数double Distance( Point& a, Point& b) double dx=a.X-b.X; double dy=a.Y-b.Y; return sqrt(dx*dx+dy*d

116、y);int main() Point p1(3.0, 5.0), p2(4.0, 6.0); double d=Distance(p1, p2); coutThe distance is dendl; return 0;友元函数友元函数n n友元函数的说明可以放在类中的任何位置,public部分、private部分、或protected部分n n它们具有相同的含义,因为友元函数不是类的它们具有相同的含义,因为友元函数不是类的成员函数成员函数n n所以,一般将友元函数说明放在public部分友元类友元类n n若类A为类B的友元类,则类A的所有成员函数都能访问类B的私有成员(私有成员数据和私有成

117、员函数)。n n定义语法:将友元类名在另一个类中使用friend修饰说明。友元类友元类class A friend class B; public: void Display() coutxendl; private: int x;class B public: void Set(int i); void Display(); private: A a;友元类友元类void B:Set(int i) a.x=i;void B:Display() a.Display();友元类友元类n n与友元函数类似,友元类的的说明可以放在类中的任何位置,public部分、private部分、或protect

118、ed部分n n它们具有相同的含义它们具有相同的含义n n所以,一般将友元函数说明放在public部分重载重载重载指的是一个标识符或运算符可同 时用于为多个函数命名, 这多个函数的 作用域相同。在C+中, 重载可分为两 种情况: 函数重载和运算符重载。重载 函数的束定是在编译时完成的, 是通过 函数原型来实现的。class Aclass A public: public:void void Print(intPrint(int i) i) coutcoutiiendlendl; ;void void Print(floatPrint(float f) f) coutcoutffendlendl;

119、 ;void void Print(constPrint(const char char s) s) coutcoutssendlendl; ;void void Print(constPrint(const char char s, s, intint n) n) coutcout&snsnendlendl; ; ;intint main() main() A a;A a;a.Print(5);a.Print(5);a.Print(4.3);a.Print(4.3);a.Print(a.Print( programprogram ); );a.Print(a.Print( programpr

120、ogram , 3);, 3); 运算符重载运算符重载运算符可以看成是一种函数,即运算 符函数。对于基本类型,运算符都是 预定义的,但对于类对象,用户可以 重新定义运算符函数,以便设置运算 符在类对象中新的含义。这种定义运 算符在某类对象中的操作的做法,就 称为运算符重载。问题举例问题举例 - 复数的运算复数的运算class Complex/复数类声明复数类声明public:Complex(double r=0.0,double i=0.0)real=r;imag=i; /构造函数构造函数void display();/显示复数的值显示复数的值private:double real;doubl

121、e imag;问题举例问题举例 - 复数的运算复数的运算n n如何实现复数的加减运算呢?n n方法1:通过函数重载实现复数的加减运算n n成员函数:成员函数:Complex Complex plus(Complexplus(Complex &) &) Complex Complex minus(Complexminus(Complex &) &)n nc1.plus(c2)c1.plus(c2) c1.minus(c2) c1.minus(c2)n n缺点:不符合通常的表达方式缺点:不符合通常的表达方式问题举例问题举例 - 复数的运算复数的运算n n方法2:重载“+”、“-”运算符n nc1

122、+ c2, c1 c2c1 + c2, c1 c2n n符合通常的表达方式符合通常的表达方式n n如果没有重载如果没有重载ComplexComplex类的类的“ “+ +” ”、“ “- -” ”运算运算符,上述表达式是错误的,因为符,上述表达式是错误的,因为C+C+中预定义中预定义的运算符其运算对象只能是基本数据类型,而的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)不适用于用户自定义类型(如类)规则和限制规则和限制n n可以重载可以重载C+C+中除下列运算符外的所有运算符:中除下列运算符外的所有运算符:n n成员运算符:成员运算符:. . n n指针取内容运算法:指针

123、取内容运算法:* * n n作用域分辨符:作用域分辨符:: : n n三目运算符:三目运算符:? :? :n n# #n nsizeofsizeof() ()n n只能重载只能重载C+C+语言中已有的运算符,不可臆造新的语言中已有的运算符,不可臆造新的n n不改变原运算符的优先级和结合性。不改变原运算符的优先级和结合性。n n不能改变操作数个数。不能改变操作数个数。n n经重载的运算符,其操作数中至少应该有一个是自经重载的运算符,其操作数中至少应该有一个是自定义类型。定义类型。操作符重载的形式操作符重载的形式n n重载为类成员函数。n n重载为友元函数。运算符函数重载运算符函数重载n n定义形

124、式函数类型 operator 运算符(形参) .class Complex Complex operator + (const Complex &);Complex operator (const Complex &);运算符函数重载运算符函数重载n n实现Complex complex:operator + (const Complex & r) Complex t; t.real = real + r.real; t.imag = imag + r.imag; return t;Complex complex:operator + (const Complex & r) return Co

125、mplex(real + r.real, imag + r.imag); 可以简化为需要定义构造函数Complex(double, double)运算符函数重载运算符函数重载n n调用的形式调用的形式n nc1 + c2;c1 + c2;n nc1.operator+(c2) c1.operator+(c2) 不推荐这种方式,但是正确不推荐这种方式,但是正确n n重载为类成员函数时重载为类成员函数时 参数个数参数个数= =原操作数个数原操作数个数-1-1 (后置(后置+、- -除外)除外)n n重载为友元函数时重载为友元函数时 参数个数参数个数= =原操作数个数原操作数个数,且,且至少应该有一

126、个自定义类型的形参。至少应该有一个自定义类型的形参。n n如果如果void display()void display()是是ComplexComplex的成员函数时的成员函数时n n(c1 + c2).display();(c1 + c2).display();运算符友元函数运算符友元函数n n如果需要重载一个运算符,使之能够用于操作某类对象的私有成员,可以此将运算符重载为该类的友元函数。n n函数的形参代表依自左至右次序排列的各操作数。n n后置单目运算符 +和-的重载函数,形参列表中要增加一个int,但不必写形参名。运算符友元函数运算符友元函数#includeclass complex

127、/复数类声明复数类声明public: /外部接口外部接口complex(double r=0.0,double i=0.0) real=r; imag=i; /构造函数构造函数 friend complex operator + (complex c1,complex c2); friend complex operator - (complex c1,complex c2);void display(); /显示复数的值显示复数的值private:/私有数据成员私有数据成员double real;double imag;运算符友元函数运算符友元函数complex operator +comp

128、lex operator +(complex c1,complex c2)(complex c1,complex c2)/ /运算符重运算符重载友元函数实现载友元函数实现 return return complex(c2.real+c1.real, c2.imag+c1.imag); complex(c2.real+c1.real, c2.imag+c1.imag); complex operator -(complex operator -(complex c1,complex c2)complex c1,complex c2) / /运算符重载友元运算符重载友元函数实现函数实现 retur

129、nreturn complex(c1.real-c2.real, c1.imag-c2.imag); complex(c1.real-c2.real, c1.imag-c2.imag); 类的继承与派生类的继承与派生n n被继承的已有类称为基类(或父类)。n n派生出的新类称为派生类。继承与派生问题举例继承与派生问题举例继承与派生问题举例继承与派生问题举例继承与派生问题举例继承与派生问题举例继承与派生的目的继承与派生的目的n n继承的目的:实现代码重用。n n派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。派生类的定义派生类的定义class 派生类名:

130、继承方式 基类名 成员定义;例子例子class Person public: Person (); Person (Person &); Person(); int Age();private: char name20; Date birthday; boolean sex;protected: char title10; class Student : public Person public: Student(); Student(Student &); Student();private: int sno; char dept20;基类派生类类成员的访问控制类成员的访问控制n n三种继承

131、方式n n公有继承公有继承n n私有继承私有继承n n保护继承保护继承n n当继承方式缺省时为私有继承方式公有继承公有继承(public)n n基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可访问。n n派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。n n通过派生类的对象只能访问基类的public成员。公有继承举例公有继承举例 - 1class A public: int a;Private: int b;Protected: int c;class B : public A

132、 public: int d; int f() int i = a; / OKAY i = b; / incorrect i = c; / OKAY Private: int e;Protected: int f;void main() B bb; int j = bb.c; / incorrect j = bb.a; / okay私有继承私有继承(private)n n基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可访问。n n派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的privat

133、e成员。n n通过派生类的对象不能访问基类中的任何成员。私有继承举例私有继承举例class Rectangle:private Location public: void InitR(int x,int y,int w,int h); void Move(int xOff,int yOff); int GetX() return Location:GetX(); int GetY() return Location:GetY(); int GetH() return H; int GetW() return W;private: int W,H;私有继承举例私有继承举例void Rectang

134、le:InitR(int x,int y,int w,int h) InitL(x,y);W=w;H=h;void Rectangle:Move(int xOff,intyOff)Location:Move(xOff,yOff);私有继承举例私有继承举例int main() /通过派生类对象只能访问本类成员通过派生类对象只能访问本类成员 Rectangle rect;rect.InitR(2,3,20,10);rect.Move(3,2); / rect.Location:Move(3,2); coutrect.GetX(), rect.GetY(), rect.GetH(), rect.Ge

135、tW()endl;return 0;保护继承保护继承(protected)n n基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可访问。n n派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。n n通过派生类的对象不能访问基类中的任何成员 private成员的特点与作用成员的特点与作用n n一个类的private成员(数据和函数)能够被该类的成员所访问n n一个类的对象不能访问和调用该类的private成员n n一个类的private成员对派生类是不可访问的protec

136、ted 成员的特点与作用成员的特点与作用n n对建立其所在类对象的模块来说(水平访问时),它与 private 成员的性质相同。n n对于其派生类来说(垂直访问时),它与 public 成员的性质相同。n n既实现了数据隐藏,又方便继承,实现代码重用protected 成员举例成员举例class A protected: int x;int main() A a; a.X=5; /错误错误class A protected: int x;class B: public A public: void Function();void B:Function() X=5; /正确正确单继承与多继承单继

137、承与多继承n n单继承 ABn n派生类只从一个基类派生。派生类只从一个基类派生。n n多重继承 (A, B)Cn n派生类从多个基类派生。派生类从多个基类派生。n n多重派生 AB ACn n由一个基类派生出多个不同的派生类。由一个基类派生出多个不同的派生类。n n多层派生 :A B Cn n派生类又作为基类,继续派生新的类。派生类又作为基类,继续派生新的类。多继承时派生类的定义多继承时派生类的定义class 派生类名:继承方式1 基类名1,继承方式2 基类名2,. 成员定义;注意:每一个“继承方式”,只用于限制对紧随其后之基类的继承。多继承举例多继承举例class Aclass A pub

138、lic: public: void void setA(intsetA(int); ); void void showAshowA();(); private: private: intint a; a; ;class Bclass B public: public: void void setB(intsetB(int); ); void void showBshowB();();private:private: intint b; b; ;class C : public A, class C : public A, privateprivate BB public: public: vo

139、id void setC(intsetC(int, , intint, , intint); ); void void showCshowC();(); private: private: intint c; c; ;多继承举例多继承举例void void A:setA(intA:setA(int x) x) a=x; a=x; void void B:setB(intB:setB(int x) x) b=x; b=x; void void C:setC(intC:setC(int x, x, intint y, y, intint z) z) / /派生类成员直接访问基类的派生类成员直接访问

140、基类的 / /公有成员公有成员 SetA(xSetA(x); ); SetB(ySetB(y); ); c=z; c=z; / /其它函数实现略其它函数实现略intint main() main() C C objobj; ; obj.setA(5); obj.setA(5); obj.showAobj.showA();(); obj.setC(6,7,9); obj.setC(6,7,9); obj.showCobj.showC();(); obj.setB(6); obj.setB(6); / / 错错误误 obj.showBobj.showB(); (); / / 错错误误 return

141、 0;return 0; 派生类的构造、析构函数派生类的构造、析构函数n n基类的构造函数不被继承,需要在派生类中自行定义。n n定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化由基类完成。单一继承时的构造函数单一继承时的构造函数派生类名:派生类名(基类所需的形参,本类成员所需的形参) : 基类名(参数) 本类成员初始化赋值语句;单一继承时的构造函数举例单一继承时的构造函数举例#includeclass B public: B(); B(int i); B(); void Print() const; private: int b;单一继承时的构造函数举例单一继承时

142、的构造函数举例B:B()b=0;coutBs default constructor called.endl;B:B(int i)b=i; coutBs constructor called. endl;单一继承时的构造函数举例单一继承时的构造函数举例B:B()coutBs destructor called.endl;void B:Print() constcoutbendl;单一继承时的构造函数举例单一继承时的构造函数举例class C:public B public: C(); C(int i,int j); C(); void Print() const;private: int c;

143、单一继承时的构造函数举例单一继承时的构造函数举例C:C()c=0;coutCs default constructor called.endl;C:C(int i,int j):B(i)c=j;coutCs constructor called.endl;单一继承时的构造函数举例单一继承时的构造函数举例C:C()coutCs destructor called.endl;void C:Print() constB:Print();coutcendl;单一继承时的构造函数举例单一继承时的构造函数举例int main()C obj(5,6);obj.Print();return 0;输出结果:Bs

144、 constructor called.Cs constructor called.56Cs destructor called.Bs destructor called.多继承时的构造函数多继承时的构造函数派生类名:派生类名(基类1形参,基类2形参,.基类n形参,本类形参):基类名1(参数), 基类名2(参数), .基类名n(参数) 本类成员初始化赋值语句;二义性问题二义性问题n n在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)采用虚函数(或同名覆盖原则来解决。n n当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,

145、将产生二义性采用虚基类来解决。n n这种继承关系构成了一个类格这种继承关系构成了一个类格二义性问题举例二义性问题举例 - 1class Aclass A public: public: void f(); void f(); ;class Bclass B public: public: void f(); void f(); void g(); void g(); ;class C: public A, class C: public A, piblicpiblic B B public: public: void g(); void g(); void h(); void h(); ;如果

146、定义:如果定义:C c1;C c1;则则 c1.f(); c1.f(); 具有二义性具有二义性而而 c1.g(); c1.g(); 无二义性(无二义性(同名覆同名覆盖盖)二义性的解决方法二义性的解决方法n n解决方法一:用类名来限定c1.A:f() 或 c1.B:f()n n解决方法二:同名覆盖在C 中定义一个同名成员函数f(),f()再根据需要调用 A:f() 或 B:f()同名覆盖原则同名覆盖原则n n当派生类与基类中有相同成员时:n n若未强行指明,则通过派生类对象使用的是派若未强行指明,则通过派生类对象使用的是派生类中的同名成员。生类中的同名成员。n n如要通过派生类对象访问基类中被覆

147、盖的同名如要通过派生类对象访问基类中被覆盖的同名成员,应使用基类名类限定。成员,应使用基类名类限定。二义性问题举例二义性问题举例 - 2class Bclass B public: public: intint b; b; class B1 : public Bclass B1 : public B private: private: intint b1; b1; class B2 : public Bclass B2 : public B private: private: intint b2; b2; ;class C : public B1,public B2class C : publ

148、ic B1,public B2 public: public: intint f(); f(); private: private: intint d; d; C c;c.b / 二义性二义性c.B:b / 二义性二义性c.B1:b /无二义性无二义性c.B2:b /无二义性无二义性二义性问题举例二义性问题举例 - 2n n派生类派生类C C的对象的存储结构示意图:的对象的存储结构示意图:n n除了二义性问题外,基类成员产生多个拷贝除了二义性问题外,基类成员产生多个拷贝bb1bb2dB类成员B类成员B1类成员B2类成员C类对象多态性的概念多态性的概念n n多态性是面向对象程序设计的重要特征之一

149、。多态性是面向对象程序设计的重要特征之一。n n多态性是在不同类型的对象上调用相同的函数导多态性是在不同类型的对象上调用相同的函数导致完全不同的行为致完全不同的行为。n n多态的体现多态的体现n n函数重载函数重载n n运算符重载运算符重载n n虚函数虚函数n n多态性的实现多态性的实现n n静态联编静态联编n n动态联编动态联编静态联编与动态联编静态联编与动态联编n n联编:n n程序自身彼此关联的过程,确定程序中的操作程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。调用与执行该操作的代码间的关系。n n静态联编(静态束定)n n联编工作出现在编译阶段,用对象名或者类

150、名联编工作出现在编译阶段,用对象名或者类名来限定要调用的函数。来限定要调用的函数。- - 编译时就确定了函编译时就确定了函数的调用关系数的调用关系n n动态联编n n联编工作在程序运行时执行,在程序运行时才联编工作在程序运行时执行,在程序运行时才确定将要调用的函数。确定将要调用的函数。- - 在运行时才确定函在运行时才确定函数的调用关系,有时也称为迟数的调用关系,有时也称为迟( (滞滞) )后联编后联编静态联编与动态联编静态联编与动态联编 - 例例1#includeclass Point public: Point(double i, double j) x=i; y=j; double Ar

151、ea() const return 0.0; private: double x, y;class Rectangle: public Point public:Rectangle(double i, double j, double k, double l);double Area() const return w*h; private:double w,h;静态联编与动态联编静态联编与动态联编 - 例例1Rectangle:Rectangle(double i, double j, double k, double l) :Point(i,j)w=k; h=l; void fun(Poin

152、t &s)coutArea=s.Area()endl; void main()Rectangle rec(3.0, 5.2, 15.0, 25.0);fun(rec);运行结果:运行结果:Area=0s.Area():属于静态联编静态联编与动态联编静态联编与动态联编 - 例例2#includeclass Point public: Point(double i, double j) x=i; y=j; virtual double Area() const return 0.0; private: double x, y;class Rectangle:public Point public:

153、 Rectangle(double i, double j, double k, double l); virtual double Area() const return w*h; private: double w,h;/其它函数同前例其它函数同前例静态联编与动态联编静态联编与动态联编 - 例例2void fun(Point &s)coutArea=s.Area()endl; void main()Rectangle rec(3.0, 5.2, 15.0, 25.0);fun(rec);运行结果运行结果:Area=375s.Area():虚函数属于动态联编虚函数虚函数n n虚函数是动态联编

154、的基础。虚函数是动态联编的基础。n n不能定义为静态的成员函数。不能定义为静态的成员函数。n n在类的定义中,在函数原型之前写在类的定义中,在函数原型之前写virtualvirtual。n nvirtualvirtual 只用来说明类定义中的原型,不能用在只用来说明类定义中的原型,不能用在函数实现时。函数实现时。n n具有继承性,基类中定义了虚函数,派生类中无具有继承性,基类中定义了虚函数,派生类中无论是否说明,同原型函数(同名同参)都自动为论是否说明,同原型函数(同名同参)都自动为虚函数。虚函数。n n本质:不是重载定义而是覆盖定义。本质:不是重载定义而是覆盖定义。n n调用方式:通过基类指

155、针或引用,执行时会调用方式:通过基类指针或引用,执行时会根据根据指向或引用的对象指向或引用的对象,决定调用哪个函数。,决定调用哪个函数。虚函数的例子虚函数的例子 #include class B0/基类B0声明public:/外部接口virtual void display() coutB0:display()endl; /虚成员函数;虚函数的例子虚函数的例子 class B1: public B0/公有派生 public: void display() coutB1:display()endl; ;class D1: public B1/公有派生 public: void display()

156、 coutD1:display()display(); -display(); void main()void main() / /主函数主函数 B0 B0 b0b0, *p;, *p;/ /声明基类对象和指针声明基类对象和指针B1 B1 b1b1; ;/ /声明派生类对象声明派生类对象D1 D1 d1d1; ;/ /声明派生类对象声明派生类对象p=p=&b0&b0; ;fun(fun(p p); );/ /调用基类调用基类B0B0函数成员函数成员p=p=&b1&b1; ;fun(fun(p p); );/ /调用派生类调用派生类B1B1函数成员函数成员p=p=&d1&d1; ;fun(fun

157、(p p); );/ /调用派生类调用派生类D1D1函数成员函数成员 虚函数的例子虚函数的例子 程序的运行结果为:B0:display()B1:display()D1:display()纯虚函数纯虚函数class Xvirtual 返回类型返回类型 标识符标识符(参数声明参数声明) =0; 抽象类抽象类n n带有纯虚函数的类成为抽象类n n不能产生或生成抽象类的对象n n抽象类只能作为基类来使用n n继承接口,类似于分布式计算中的继承接口,类似于分布式计算中的InterfaceInterface的概的概念念n n从抽象类中派生出的类必须为纯虚拟函数提供实现,否则它们也是抽象类。抽象类的作用抽象

158、类的作用n n抽象类为抽象和设计的目的而建立,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。n n对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。抽象类的例子抽象类的例子#include class B0 /抽象基类抽象基类B0声明声明public: /外部接口外部接口virtual void display( )=0; /纯虚函数成员纯虚函数成员;抽象类的例子抽象类的例子class B1: public B0class B1: public B0/ /公有派生公有派生 public:public:void void displaydisplay() ()

159、 coutcoutB1:display()B1:display()endlendl; ; / /虚成员函数虚成员函数; ;class D1: public B1class D1: public B1/ /公有派生公有派生 public:public:void void displaydisplay() () coutcoutD1:display()D1:display()display(); -display(); void main()void main() / /主函数主函数主函数主函数 B0 *p;B0 *p;/ /声明抽象基类指针声明抽象基类指针声明抽象基类指针声明抽象基类指针B1 B

160、1 b1b1; ;/ /声明派生类对象声明派生类对象声明派生类对象声明派生类对象D1 D1 d1d1; ;/ /声明派生类对象声明派生类对象声明派生类对象声明派生类对象p=p=&b1&b1; ;fun(fun(p p); );/ /调用派生类调用派生类调用派生类调用派生类B1B1函数成员函数成员函数成员函数成员p=p=&d1&d1; ;fun(fun(p p); );/ /调用派生类调用派生类调用派生类调用派生类D1D1函数成员函数成员函数成员函数成员 抽象类的例子抽象类的例子程序的运行结果为:B1:display()D1:display()模板模板(Template)模板函数模板函数n n模

161、板函数的引入n n考虑考虑 y = |x|y = |x|n n首先考虑到的是各种能够处理的类型进行重载首先考虑到的是各种能够处理的类型进行重载int absolute ( int x ) return x 0 x : -x;double absolute ( double x ) return x 0 x : -x;float absolute ( float x ) return x 0 x : -x;如此下去可能会需要很多这样的函数,显得很繁琐模板函数模板函数n n上述函数组的特点n n函数体完全一样函数体完全一样n n仅仅是函数的参数类型不同仅仅是函数的参数类型不同n n成为参数化多态性

162、成为参数化多态性n n这样的函数组当然可以用宏来解决上述问题n n#define #define absolute(xabsolute(x) (x)0?(x):)(-x) (x)0?(x):)(-x)n n如前所述,宏在如前所述,宏在C+C+中并不是一种推荐的编程中并不是一种推荐的编程风格,也存在各种各样的问题风格,也存在各种各样的问题n n前面介绍过的内联函数也无法解决这一问题前面介绍过的内联函数也无法解决这一问题模板函数模板函数n n引入模板函数的概念n n解决参数化多态性的程序设计问题解决参数化多态性的程序设计问题n n模板函数的定义n n返回类型可以是模板的类型,也可以是其他类型Tem

163、plate 类型名 函数名(参数表) 函数体的定义 Template 类型名 函数名(参数表) 函数体的定义 模板函数模板函数#include Template T absolute( T x ) return x 0 ? x : -x; void main() cout “n absolute(-99)=” absolute(-99); cout “n absolute(-99.99)=” absolute(-99.99); cout “n absolute()=” absolute();模板函数模板函数n n共有三次函数调用n nabsolute(-99)absolute(-99):函数原

164、型为:函数原型为intint absolute(intabsolute(int x) x)n nabsolute(-99.99) absolute(-99.99) :调用的函数原型为:调用的函数原型为double double absolute(doubleabsolute(double x) x)n nabsolute(absolute( ) ) :函数原型为:函数原型为char char absolute(charabsolute(char x) x)n n多类型模板函数定义Template 类型名 函数名(参数表) 函数体的定义 Template 类型名 函数名(参数表) 函数体的定义

165、模板函数模板函数 - 例例1#include Templatevoid outputArray( const T * P_array, const int count) for( int i=0; icount; i+) count P_arrayi “ ”; cout endl;void main() const int aCount = 8, bCount = 8, cCount = 20; int aArrayaCount = 1, 2, 3, 4, 5, 6, 7, 8; double bArray bCount = 1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8; c

166、har cArray cCount = “Welcome to see you!”;模板函数模板函数 - 例例1 cout “a Array contains;” endl; outputArray( aArray, aCount); cout “b Array contains;” endl; outputArray( bArray, bCount); cout “c Array contains;” endl; outputArray( cArray, cCount);运行结果:A Array contain:1 2 3 4 5 6 7 8A Array contain:1.1 2.2 3

167、.3 4.4 5.5 6.6 7.7 8.8A Array contain:Welcom to see you!模板函数模板函数 - 例例2#include Template T1 * add( T1 data, T2 d, int n) for(int i = 0; i n; i+) datai += d; return data;void main() float a10 = 1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2; add(a, 1, 5); for(int i = 0; i 10; i+) cout “ ” ai;模板函数模板函数 - 例例2n n输

168、出结果:n n2.1 2.2 2.3 2.4 2.5 1.6 1.7 1.8 1.9 22.1 2.2 2.3 2.4 2.5 1.6 1.7 1.8 1.9 2n n在模板函数中存在一个强制类型转换n ndataidatai += d; += d; n nDataiDatai += T1(d); += T1(d);n n模板函数的前向说明,与定义类似n nTemplate Template 类型名类型名 函数名(参数表)函数名(参数表)n nTemplate Template n nT1 * add(T1 data, T2 d, T1 * add(T1 data, T2 d, intint

169、n); n);模板函数的特例与重载模板函数的特例与重载n n对于上面的absolute模板函数n n要支持要支持complexcomplex类型变量类型变量n nAbsolute( complex(3, 4);Absolute( complex(3, 4);n n存在的问题是:复数绝对值的概念复数的模值,存在的问题是:复数绝对值的概念复数的模值,不同于简单类型的绝对值,这样模板函数的函不同于简单类型的绝对值,这样模板函数的函数体实现是不同的数体实现是不同的, , 这种情况称为特例这种情况称为特例n nC+C+提供编写特例的机会,可以为提供编写特例的机会,可以为complexcomplex类编类

170、编写一个求复数绝对值写一个求复数绝对值( (求模求模) )的特例函数的特例函数模板函数的特例与重载模板函数的特例与重载Template T absolute(T x) return x 0 ? X : -x;double absolute( complex cx) return sqrt(real(cx) * real(cx) + imag(cx) * imag(cx);模板函数的特例模板函数的特例与重载模板函数的特例与重载n n对于一个模板函数调用n nabsolute(-99)absolute(-99)n nabsolute(-99.99)absolute(-99.99)n nabsolu

171、te(complex(3,4)absolute(complex(3,4)n n编译器首先检查是否有特例函数可以调用,然编译器首先检查是否有特例函数可以调用,然后再检查模板函数后再检查模板函数模板函数的特例与重载模板函数的特例与重载n n模板函数也可以重载模板函数也可以重载n n模板函数的重载和普通函数一样没有任何不同模板函数的重载和普通函数一样没有任何不同Template T max(T x, T y) return x =y ? X : y;Template T max(T x, T y, T z) T w = (x=y)? X:y; return(w=z)?w”z;模板函数的特例与重载模板

172、函数的特例与重载Template T max(T x, int n) T maxinum = x0; for(int i=0; in; i+) if (maxinum xi) maxinum = xi; return maxinum;和普通函数一样,编译器会根据参数的不同来联编 不同的模板函数起泡排序模板函数起泡排序模板函数n n对具有n个元素的序列按升序进行起泡排序的步骤:n n首先将第一个元素与第二个元素进行比较,若为逆首先将第一个元素与第二个元素进行比较,若为逆序,则将两元素交换。然后比较第二、第三个元素,序,则将两元素交换。然后比较第二、第三个元素,依次类推,直到第依次类推,直到第n-

173、1n-1和第和第n n个元素进行了比较和交个元素进行了比较和交换。此过程称为第一趟起泡排序。经过第一趟,最换。此过程称为第一趟起泡排序。经过第一趟,最大的元素便被交换到第大的元素便被交换到第n n个位置。个位置。n n对前对前n-1n-1个元素进行第二趟起泡排序,将其中最大元个元素进行第二趟起泡排序,将其中最大元素交换到第素交换到第n-1n-1个位置。个位置。n n如此继续,直到某一趟排序未发生任何交换时,排如此继续,直到某一趟排序未发生任何交换时,排序完毕。对序完毕。对n n个元素的序列,起泡排序最多需要进个元素的序列,起泡排序最多需要进行行n-1n-1趟。趟。起泡排序模板函数起泡排序模板函

174、数对整数序列 8 5 2 4 3 按升序排序8524352438243582345823458初始状态第一趟结果第二趟结果第三趟结果第四趟结果小的逐渐上升每趟沉下一个最大的起泡排序模板函数起泡排序模板函数Template void BubbleSort(T A, int n) int i, j; int lastExchangeIndex; i = n 1; while ( i 0 ) lastExchangeIndex = 0; for(j = 0; ji; j+) if(Aj+1Aj) Swap(Aj, Aj+1); lastExchangeIndex = j; i = lastExcha

175、ngeIndex; Ai局部有序j模板类模板类n n模板类的需求与模板函数类似模板类的需求与模板函数类似Class IntArray int * element; int n;Public: Array(int * data, int i); Array(); void sort(); int find(int a); int sum();Class FloatArray float * element; int n;Public: Array(float * data, int i); Array(); void sort(); int find(float a); float sum();

176、模板类模板类n n模板类的定义Template Template class class 类名类名 Template class Array T * element; int n;Public: Array(T * data, int i); Array(); void sort(); int find( T a ); T sum();模板类模板类n n在类外实现模板类的成员函数Template Template 类型名类型名 类名类名:函数名函数名( (参数表参数表) )n n模板类的成员函数必须是模板函数模板类的成员函数必须是模板函数Template T Array:sum() T s =

177、 0; for ( int i = 0; i n; i +) s += elementi; return s;模板类模板类n n模板类的对象建立n n类名类名 对象对象1, ., 1, ., 对象对象n;n;Array cI(Idata, 10);Array cF(fData, 10);完整的模板数据类的实现完整的模板数据类的实现#include Template class Array T * element; int n;Public: Array(T * data, int i) : element(data), n(i) Array() void sort(); int find(T

178、key); T sum();完整的模板数据类的实现完整的模板数据类的实现Template void Array:sort() int i, j; T d; for(i=1; i=i; i-) if(elementj-1 elementj) d=elementj-1; elementj-1=elementj; elementj = d; 完整的模板数据类的实现完整的模板数据类的实现Template int Array:find( T key) for( int i=1; in; i+) if(elementi = key) return i; return -1;Template T Array

179、:sum() T s = 0; for(int i=0; in;i+) s+=elementi; return s;模板类模板类n n多个模板类型参数的模板类定义多个模板类型参数的模板类定义Template class T1, ., class Template Class Class 类名类名 . ; . ;n n在定义多类型参数模板类时,每个类型参数必须至少在定义多类型参数模板类时,每个类型参数必须至少被使用一次,否则错误被使用一次,否则错误n n外部实现成员函数外部实现成员函数 Template class T1, ., class Template 类型名类型名 类名类名T1, .,

180、:函数名函数名( (参数表参数表) ) n n对象的定义对象的定义类名类名t1, 对象名对象名( (初始化参数列表初始化参数列表); );模板类模板类Template class Data2 T1 a; T2 b;Public: Data2(T1 x, T2 y) : a(x), b(y) ; Data2() void OutIt();Template void Data2:OutIt() cout “a=” a endl; cout “b=” b endl;Data2 Da(A,5.5);模板类的特例模板类的特例n n与模板函数一样,模板类也允许模板类的特例存与模板函数一样,模板类也允许模板

181、类的特例存在,对于前面实现的模板类存在特例:在,对于前面实现的模板类存在特例:char *char *Template class Array T * element; int n;Public: Array(T *data, int i) : element(data), n(i) ; Array() ; void sort(); int find(T key); T sum(); friend ostream &operator(ostream &out, Array &array);模板类的特例模板类的特例Template void Array:sort() int i, j; T d;

182、 for(i=1; i=i; i-) if(elementj-1 elementj) d=elementj-1; elementj-1=elementj; elementj = d; 模板类的特例模板类的特例Template int Array:find( T key) for( int i=1; in; i+) if(elementi = key) return i; return -1;Template T Array:sum() T s = 0; for(int i=0; in;i+) s+=elementi; return s;模板类的特例模板类的特例Template ostream

183、&operator(ostream &out, Array &array) for(int i=0; iarray.n; i+) out “ “ array.elementi; return out;模板类的特例模板类的特例n n通过分析上述模板类的定义并不是适合于各种情况和各种数据类型n n因此有时需要编写模板类的特例n n下面是Array模板类的一个特例类的定义和实现模板类的特例模板类的特例 - char *class Array char * * element; int n;Public: Array (char * *data, int i) : element(data), n(i

184、) ; Array() ; void sort(); int find(char * key); / T sum(); friend ostream &operator(ostream &out, Array &array);模板类的特例模板类的特例 - char *void Array:sort() int i, j; char * d; for(i=1; i=i; i-) if(strcmp(elementj-1,elementj)0) d=elementj-1; elementj-1=elementj; elementj = d; 模板类的特例模板类的特例int Array:find(

185、T key) for( int i=1; in; i+) if(strcmp(elementi, key)=0) return i; return -1;Template ostream &operator(ostream &out, Array &array) for(int i=0; iarray.n; i+) out “ “ array.elementi; return out;模板栈模板栈 - 栈的数据结构及实栈的数据结构及实现现Const int MaxStackSize = 50;Template class Stack Private: T stackListMaxStackSi

186、ze; int top;Public: Stack(void); void Push( const T & item ); T Pop(void); woid ClearStack(void); T Peek(void) const; int StackEmpty(void) const; int StackFull(void) const;Template Stack:Stack(void): top(-1) Template Void Stack:Phsu(const T& item) if(top=MaxStackSize -1) std:cout“Stack overflow!n”;

187、exit( -1 ); top+; stacklisttop = item;模板栈模板栈 - 栈的数据结构及实栈的数据结构及实现现Template T Stack:Pop(void) T temp; if(top = -1) std:cout:Attemp to pop an empty stack!N”; exit( -1 ); return stacklist top ;Template int Stack:StackEmpty(void) const return top =-1; Template int Stack:StackFull(void) const return top =

188、 MaxStackSize -1; Template Void Stack:ClearStack(void) top = -1; Standard Template LibrarySTLSTL,虽然是一套程序库,虽然是一套程序库(Library)(Library),确不是一般印象中的程,确不是一般印象中的程序库,而是一个有着划时代意义,背后拥有着先进技术与深序库,而是一个有着划时代意义,背后拥有着先进技术与深厚理论的产品。说他是产品也可以,说他是规格也可以,说厚理论的产品。说他是产品也可以,说他是规格也可以,说是软件组件技术发展史上一个大突破点,它也当之无愧。是软件组件技术发展史上一个大突破点

189、,它也当之无愧。长久以来,软件界一直希望建立一种可重复运用的东西,以长久以来,软件界一直希望建立一种可重复运用的东西,以及一种可以制造出及一种可以制造出“ “可重复运用的东西可重复运用的东西” ”的方法,让工程师的方法,让工程师/ /程序员的心血不至于随时间的推移、人事异动而烟消云散。程序员的心血不至于随时间的推移、人事异动而烟消云散。从副程式从副程式(subroutines)(subroutines)、程序、程序(procedures)(procedures)、函数、函数(functions)(functions)、类别、类别(classes)(classes),到函数库,到函数库(func

190、tion (function libraries )libraries )、类别库、类别库(class libraries )(class libraries )、各种组件、各种组件(components)(components),从结构化设计、模组化设计、物件导向设计,从结构化设计、模组化设计、物件导向设计,到样式到样式(patterns)(patterns)的归纳整理,无一不是软件工程的漫漫奋的归纳整理,无一不是软件工程的漫漫奋斗史,为的就是复用性斗史,为的就是复用性( (reusebilityreusebility) )的提升。的提升。摘自摘自STLSTL源码分析源码分析Standard

191、 Template LibrarySTLSTL提供提供6 6大组件,彼此可以组合套用:大组件,彼此可以组合套用:容器容器(containers):(containers):各种资料结构,如各种资料结构,如VectorVector、ListList、MapMap等,用来存储各种数据。等,用来存储各种数据。演算法演算法(algorithms):(algorithms):各种常用的算法,如各种常用的算法,如sortsort、searchsearch、copycopy等,它的作用是为提供各种常用的操等,它的作用是为提供各种常用的操作。作。迭代器迭代器( (iteratorsiterators): ):

192、一个非常重要的组件,用来一个非常重要的组件,用来将容器和演算法联系起来。也就是通常所说的泛将容器和演算法联系起来。也就是通常所说的泛型指针。型指针。仿函数仿函数( (functorsfunctors): ):行为类似函数,可作为演算法行为类似函数,可作为演算法的某种策略的某种策略(policy)(policy)。配接器配接器(adapters):(adapters):一种用来修饰容器或仿函数界一种用来修饰容器或仿函数界面的东西。面的东西。配置器配置器( (allocatorsallocators): ):负责空间配置与管理,用来负责空间配置与管理,用来保证容器空间的正确分配。保证容器空间的正确

193、分配。VectorVector的行为方式完全是一个数组,它的内存空间完全是连续的,只不过这个数组的大小是可变的。Vector的实现:在初始化的时候,会申请一定的空间用来存储数据,一旦所申请的空间不够用了,它会在另外的地方开辟一块新的内存空间,然后将原空间中的元素全部拷贝到新的空间中,如果不特殊指定,每次新申请的通常是原空间的两倍。List相对于Vector的线性存储空间,List就复杂的多,它每次添加或者删除一个元素,就会申请或者释放一个元素的空间,然后用指针将他们联系起来。这样的好处就是精确配置内存,绝对没有一点的浪费。而且对于元素的插入和删除,List都是常数时间。template template structstruct _ _list_nodelist_node TypedefTypedef void void * *void_pointervoid_pointer; ;void_pointervoid_pointerprevprev; ; / /指向前面元素的指针指向前面元素的指针void_pointervoid_pointernext;next; / /指向后面元素的指针指向后面元素的指针T Tdata;data; / /节点实体,存储数据节点实体,存储数据

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号