《C语言面向程序设计03版.ppt》由会员分享,可在线阅读,更多相关《C语言面向程序设计03版.ppt(286页珍藏版)》请在三一办公上搜索。
1、C+面向对象程序设计,目 录,第1章 C+基础第2章 类和对象(一)第3章 类和对象(二)第4章 友元第5章 运算符重载第6章 模板第7章 继承和派生第8章 虚函数和多态性第9章 C+流第10章 异常处理和名字空间第11章 C+标准模板库基础第12章 面向对象软件设计,第1章 C+基础,C+概述,计算机语言种类,计算机语言的种类非常多,总的来说可以分成机器语言、汇编语言、高级语言三大类。,计算机所能识别的语言只有机器语言,即由0和1构成的代码。但通常人们编程时,不采用机器语言,因为它非常难以记忆和识别。,目前通用的编程语言主要有两种形式:汇编语言和高级语言。,高级语言所编制的程序不能直接被计算
2、机识别,必须经过转换才能被执行,按转换方式可将它们分为解释方式和编译方式两类。,程序设计方法,程序设计是指设计、编写和调试程序的方法与过程。,1.结构化程序设计,结构化程序设计方法的核心包括自顶向下、逐步求精的开发方法、模块化的组织方式和结构化的语句结构等几方面。,结构化程序设计的特点如下。,程序设计:程序是由一个个的函数组成的,函数之间通过调用而相互作用。程序设计的主要技巧在于追踪哪些函数和调用哪些函数,哪些数据发生了变化。程序内容:由函数和函数调用构成。,结构化设计的弱点表现在抽象级别较低、封装性较差、可重用性较低等方面。,2.面向对象程序设计,面向对象程序设计方法的核心包括抽象、封装、对
3、象、类、消息和继承等几方面。,面向对象程序设计的特点如下。,程序设计:程序是由一个个的对象组成的,对象之间通过消息而相互作用。程序设计的主要技巧在设计哪些类以及类之间的关系。程序内容:由类对象和消息传递构成。,面向对象程序设计的优点表现在实现对现实世界客体的自然描述、可控制程序的复杂性、可增强程序的模块性、可提高程序的重用性和可改善程序的可维护性等方面。,C+语言及其特点,1.C+语言的起源,C+语言是一种高级语言,它是对C语言的扩展,是C语言的超集。贝尔实验室的Bjarne Stroustrup在C语言的基础上,创建了C+语言。,C+语言的ISO标准已在1997年11月被一致通过,1998年
4、8月被正式批准。,2.C+语言的特点,(1)C+支持数据封装(2)C+类中包含私有、公有和保护成员(3)C+中通过发送消息来处理对象(4)C+中允许友元破坏封装性(5)C+允许函数名和运算符重载(6)C+支持继承性(7)C+支持动态绑定,C+程序的基本结构,C+程序的基本结构如下。,(1)C+程序由函数组成(2)C+函数由函数首部与函数体两部分组成(3)C+程序的书写格式(4)C+的输入输出(5)C+注释(6)编译预处理命令,C+程序的开发步骤,C+是一种编译性的语言,设计好一个C+源程序后,需要经过编译、连接,生成可执行的程序文件,然后执行并调试程序。C+程序的开发步骤如下:,(1)分析问题
5、(2)编辑程序(3)编译程序(4)连接程序(5)运行调试程序,C+程序开发步骤如图所示。,C+语言初识,数据类型,C+的基本数据类型有5种:整型(int)、浮点型(float)、字符型(char)、双精度浮点型(double)和无值型(void)。,C+提供的5种基本数据类型,其数据的长度和范围会随处理器的类型和编译器类型的不同而异。一般来说,字符类型为1个字节长;整数类型与CPU字长相等,一般为2个字节或4个字节长;浮点数的长度一般为整型数的2倍;双精度类型字长为浮点型的2倍。,C+的修饰符有short、long和unsigned。,下表列出了大多数32位系统中内置数据类型的范围。,常量,常
6、量是在程序中不能改变的量。C+支持4种类型的常量:浮点型、整型、字符型和枚举型。,变量,顾名思义,变量就是值可变的量。一个变量有三个要素:,变量由一个变量名惟一标识,也就是说,每个变量有一个变量名。变量可以保存某个数据值,而数据值有相应的数据类型,所以每个变量又具有一个特定的数据类型。数据类型标志着这个变量将来的用法以及它将保存哪种类型的数据值。变量存储在内存中,有对应的地址,也就是说,每个变量有一个地址,可以进行取地址等操作,而其他表达式如x+y不能进行取地址操作。,(1)变量的命名(2)变量的定义和声明(3)变量的初始化,数据的输入和输出,输入输出是程序的基本功能。C+的标准输入/输出流库
7、用于数据的输入输出,用于输入输出的流库包含在头文件iostream.h中。,1.输出,C+定义了运算符“”的iostream类,而“”就是用于处理内部类型的输出。,(1)显示常数,使用cout可以显示字符串和数字,并可显示多个值。,(2)八进制和十六进制数的输出,在某些情况下,程序需要以八进制或十六进制数的形式输出。C+提供了简单的实现方法:只要在输出流中输出操纵符dec(十进制)、oct(八进制)或hex(十六进制)即可。,(3)控制输出宽度,C+提供的setw操纵符可以指定每个数值占用的宽度,即这个字符占用的最小字符长度。,(4)控制输出精度,C+提供的setprecision操纵符可以设
8、置显示数值的精度。,2.输入,C+提供了输入流cin,可以利用输入流cin读取键盘输入的字符和数值,并把它赋给指定的变量。从键盘上输入的数据通过cin接收,再由提取运算符“”送到程序指定的变量中。因此,用户输入数据时要避免输入的数据超出指定变量的值域。,控制语句,顺序控制语句,所谓顺序结构,就是按照语句的顺序一条一条地执行。顺序控制语句是一类简单的语句,包括表达式语句、复合语句、空语句和输入输出语句等。,选择控制语句,除简单的顺序控制语句外,C+还定义了一些可以控制程序执行流程的语句,这些语句提供对控制流的选择和循环功能。C+中,语句默认都是顺序执行,如果碰到选择或循环语句,顺序执行的规则就要
9、改变。,C+中的选择控制语句有if语句、ifelse语句、ifelse if语句和switch语句。,循环控制语句,循环控制语句提供重复处理的能力,当某一特定条件为真时,循环语句就重复执行,并且每循环一次,就会测试一下循环条件,如果为假,则循环结束,否则继续循环。,C+支持三种格式的循环控制语句:while、do和for语句。三者可以完成类似的功能,不同的是它们控制循环的方式。,跳转语句,除了顺序执行和选择、循环控制外,有时需要中断一段程序的执行,跳转到其他地方继续执行,这时需用到跳转语句,包括break、continue和goto语句。,构造数据类型,数组类型,数组是具有相同数据类型的元素序
10、列。在内存中,它占据一组连续的内存位置。数组的每一项都是一个变量,称为元素。每个元素的存取是通过数组名加偏移来实现的。实际上,数组是一组相关的内存位置,它们都具有相同的名字和类型。为了引用数组中的特定位置或元素,需指定数组名和数组中特定元素的位置编号。,数组是程序设计语言中常用的数据结构之一。当若干数据具有相同的数据类型并且互相有一定关系时,把它们组织成数组非常有效。,和其他变量一样,在使用数组之前需要对数组进行定义。数组分为一维数组、二维数组和三维及以上的数组,通常把二维数组称为矩阵,三维及以上的数组称为多维数组。,枚举类型,枚举类型定义了一些整型符号常量的集合,其格式如下:,enum 类型
11、名 标识符1,标识符2,标识符n;,其中,“标识符1”至“标识符n”表示一些整型符号常量,它们默认由系统赋予整数值:“标识符1”的值为0,“标识符2”的值为1,“标识符n”的值为n-1,序列中每个标识符的值比前一个标识符大1。,程序员也可以自己定义标识符的值,定义方式是直接在定义语句的标识符后赋值。,结构体类型,在日常生活中经常会遇到这样的情况,由一些相关的数据共同表示一个信息。例如,表示一个学生的信息,可以包括学生的学号、姓名、性别、出生日期、班号等相关的数据项,这些数据项的组合表示一个学生的个人情况,它们之间是有内在联系的,任何一个单独的信息都不能完整地表示学生的信息。在C+中用结构体来表
12、示这些相关的信息。构成结构体的成员的数据类型可以不同,它们形成了一个整体,成为一个新的数据类型,称为结构体类型。结构体类型由各个成员构成,有时也将这些成员称为数据域,简称为域。,结构体类型和结构体变量的声明格式如下:,struct 结构体名/结构体类型声明 成员类型 成员1;成员类型 成员2;.成员类型 成员n;结构体变量名/结构体变量定义,其中,“结构体名”和“结构体变量名”都可以省略。省略前者表示声明了一个无名结构体类型和定义了具有该类型的变量,省略后者表示仅仅声明了一个结构体类型。,共用体类型,共用体的定义形式和用法类似于结构体,但结构体中的每个成员在内存中占有独立的存储位置,而共用体的
13、各个成员共享一块内存,所以,在任意时刻只能有一种数据类型的变量是活跃的。,共用体类型和共用体变量的声明格式如下:,union 共用体名/共用体类型声明 成员类型 成员1;成员类型 成员2;.成员类型 成员n;共用体变量名;/共用体变量定义,其中,“共用体名”和“共用体变量名”都可以省略。省略前者表示声明了一个无名共用体类型和定义了具有该类型的共用体变量,省略后者表示仅仅声明了一个共用体类型。,用户自定义类型,在C+中可以利用typedef定义自己的变量类型,其格式如下:,typedef 类型声明;,其中,“类型声明”类似于变量声明,只是变量名用类型名代替了,实际上,这等于定义了一个同义词,这种
14、方式与创建一个新的类型是不同的。,使用自定义类型有几点好处:一是可以更好地表达程序员的意思,如用width来表示将要定义的标识符是属于宽度一类的数据,用string表示程序员想要定义的是一个字符串,这比直接用系统预定义的类型清晰得多;二是简单方便,一些数据类型可以用一个简单的类型标识符来表示,不用每次都麻烦地写复杂的定义。,指针,指针的定义,定义指针时必须指明指针所指对象的数据类型,且在变量名前加上星号“*”表示这是一个指针。当定义多个同一类型的指针时,用逗号隔开各变量标识符,且每个变量前都要加上星号。,一个指针所占用的内存空间大小与一个内存地址所占空间等同,因而,一个浮点型指针和一个字符型指
15、针所占内存大小相同。指针的类型仅用于告知编译系统如何将指针所指的二进制序列翻译成实际的数据。,指针的初始化,指针初始化有下列几种方式。,(1)指针对象可以被一个具有相同类型的对象初始化。,(2)由另一个同一类型的指针初始化,同一类型的指针之间可以直接赋值。,(3)通过直接分配内存地址得到初值。,指针的运算符,定义指针变量的目的是,通过指针变量间接地访问变量。为此C+提供了两个指针运算符。,*:取指针值运算符。根据指针所指内存单元地址间接地访问对应存储单元,若指针变量p指向变量a,则*p运算的结果为变量a的值,即*p表示变量a的内容。&:取地址运算符。返回变量对应的存储单元地址,若a为int变量
16、,p为int型指针变量,则p=&a表示将a的存储单元地址赋给p,或者指针变量p指向变量a。,指针和数组的关系,在C+中,数组和指针密切相关,几乎可以互相使用。数组名字可以认为是常量指针,它指向存放数组第一个元素的内存地址。指针可以用于完成任何涉及数组下标的操作。,实际上,数组下标表示法在编译期间将转换为指针表示法,所以用指针方式来书写数组下标表达式可以节省编译时间。,虽然数组名和指针在引用数组元素和取得它们的地址方面可以互换,但两者有一个重要的不同点:数组名是在定义时就已分配好了内存空间的,因此,数组名是一个地址常量,在程序中不能将数组名作为变量为其赋值,而指针是一个变量,可以多次赋值,注意:
17、,new与delete,尽管指针与数组具有相似性,但两者之间是有区别的:数组在定义时就根据指定的大小分配好了内存空间,而指针只是一个变量。,利用new运算符给指针分配一片内存空间,new运算符与delete运算符一起使用,以达到直接进行动态内存分配和释放的目的。,需要注意以下几点:,(1)运算符delete必须用于先前new分配的有效指针。,(2)用new也可指定分配的内存大小。,(3)new可以为数组分配内存,但当释放时,必须告诉delete数组有多少个元素。,函数,函数的定义和调用,在使用函数时,要先对函数进行定义,确定它要实现的功能。函数的使用就是调用函数的过程。在函数定义和调用中,把传
18、递给函数的值称为实际参数,把函数内部用来接收传递给函数的值的变量称为形式参数。,1.函数定义,C+中每一个函数的定义都是由4个部分组成的,即函数类型、函数名、函数参数表和函数体。,2.函数的声明,函数声明消除了函数定义的位置影响。不管函数是在何处定义的,只要在调用前进行函数的声明,就可保证函数调用的合法性。虽然函数不一定在程序的开始就声明,但为了提高程序的可读性和保证简洁的程序结构,最好将主函数main放在程序的开头,而将函数声明放在主函数main之前。,3.函数的调用,调用函数时,在函数名后跟参数(调用函数时的参数称为实际参数或实参),参数用逗号隔开。调用时,将实参中的实参值逐个代入形参,这
19、一过程称为向函数传递参数。,全局变量和局部变量,1.变量作用域,变量作用域是指变量的有效范围,一个变量只有在其有效范围内才能存取它的值。变量作用域有块级作用域、函数级作用域、文件级作用域和程序级作用域几个级别。,2.局部变量,作用域在函数级和块级的变量称为局部变量,也就是说,局部变量在一个函数或块内部定义。因此,局部变量只能在定义它的函数体或块内部使用,而不能在其他函数或块内使用这个变量。,有关局部变量的说明如下:,(1)main函数本身也是一个函数,因而在其内部声明的变量仍为局部变量,只能在main函数内部使用,而不能在其他函数中使用。,(2)在不同的函数中可声明具有相同变量名的局部变量,系
20、统会自动进行识别。,(3)形参也是局部变量,其作用域在定义它的函数内。所以形参和该函数体内的变量是不能重名的。,3.全局变量,作用域在程序级或文件级的变量称为全局变量,也就是说,全局变量是在函数外部定义和访问的。全局变量的作用域是从定义该变量的语句位置开始,直至本文件结束。因而全局变量声明后可以被很多函数使用。,有关全局变量的说明如下:,(1)全局变量的作用域是从声明该变量的位置开始直到程序结束处。,(2)全局变量的作用域为函数间传递数据提供了一种新的方法。,(3)在一个函数内部,如果一个局部变量和一个全局变量重名,则在局部变量的作用域内,全局变量不起作用。,4.变量的存储类别,变量的存储类别
21、有两种:auto(动态)和static(静态)。其使用说明如下:,(1)全局变量默认的存储类别为static,一旦定义后就分配存储空间,直到程序运行结束才释放其存储空间。,(2)局部变量默认的存储类别为auto,在遇到其定义后就分配存储空间,一旦退出其有效范围后就释放其存储空间。,(3)可以用static将局部变量的存储类别指定为static。这样局部static变量在经过不同函数调用之后,也可以保留它们原来的值。,函数的参数传递,C+中函数的参数传递分为按值传递、地址传递和引用传递几种。由于地址也是一种值,所以,按值传递和地址传递都是单向的值传递方式。,1.值传递,所谓按值传递,是指当一个函
22、数被调用时,C+根据实参和形参的对应关系将实际参数值一一传递给形参,供函数执行时使用。,2.引用传递,(1)什么是引用,引用是一个已知变量的别名,对引用的运算就是对被它关联的变量的运算,也就是说,引用和它所关联的变量享受同等的访问待遇。,(2)建立引用的方法,为建立引用,先写上被关联变量的数据类型,后跟引用运算符“&”,再是引用的名字。,(3)引用的特点,引用具有如下特点:,引用名字可以是任何合法变量名。引用必须初始化,而且初始化之后还可以成为另外同类型变量的引用。引用的类型和关联变量的类型应该是严格一致的。引用仅在声明时带有“&”,以后就像普通变量一样使用,不能再带“&”。引用的目的在于可以
23、用较清晰的方法编写改变其参数值的函数,以及接收结构体变量和对象作为参数的函数。,(4)引用作为函数参数,引用传递方式是将形参定义成对应实参的引用,即形参作为对应实参的别名,语法上只需在函数声明或定义中形参的数据类型后面加上符号“&”即可,其他语法与按值传递相同。,(5)引用返回值,所谓引用返回值是指一个函数声明为返回一个引用类型。引用作为返回值时,必须遵守以下规则:,不能返回局部变量的引用 不能返回函数内部new分配的内存的引用,(6)常引用,使用const修饰符可以声明引用,被声明的引用为常引用,不能通过常引用更新对象的值。,3.函数的默认参数值,在C+中,允许在函数的声明或定义时给一个或多
24、个参数指定默认值。这样在调用时,可以不给出参数,而按指定的默认值进行工作。,在设置函数的默认参数值时要注意以下两点:,(1)当函数既有声明又有定义后,不能再在函数定义中指定默认参数。(2)当一个函数中有多个默认参数时,则形参分布中默认参数应从右到左逐渐定义。在函数调用时,系统按从左到右的顺序将实参与形参结合,当实参的数目不足时,系统将按同样的顺序用声明中或定义中的默认值来补齐所缺少的参数。,内联函数,内联(inline)是内联扩展的简称。与一般函数不同,C+编译器在遇到调用内联函数的地方会用函数体中的代码来替换函数的调用,好处是节省函数调用带来的参数传递、运行栈的入栈与出栈等开销,从而提高运行
25、速度,但付出的代价是增加了代码长度。内联函数的定义是在一般函数定义前加上inline关键字。,函数重载,所谓函数重载是指同一个函数名可以对应多个函数的实现。例如,可以给函数名add()定义多个函数实现,该函数的功能是求和,即求两个运算数的和。其中,一个函数实现是求两个int型数之和,另一个实现是求两个浮点型数之和,再一个实现是求两个复数的和。每种实现对应着一个函数体,这些函数的名字相同,但是函数的参数类型不同。这就是函数重载的概念。,函数重载要求编译器能够惟一地确定调用一个函数时应执行哪个函数代码,即采用哪个函数实现。确定函数实现时,要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时
26、,要求同名函数在参数个数上不同,或者参数类型上不同。否则,将无法实现重载。,域运算符,以域运算符为前缀的变量表示全局变量。,例如,下面程序中有两个变量同名,一个为全局变量,一个为局部变量,可以通过域运算符访问全局变量:int var=10;/全局变量fun()int var;/局部变量 var=:var;/将全局变量的值赋给局部变量,断言,assert宏(在assert.h头文件中定义)测试表达式的值。如果表达式的值是0(假),则assert打印错误信息,并调用函数abort()以结束程序的执行。这是测试某个变量是否具有正确值的有用的调试工具,例如,以下语句就是一个断言:,assert(x10
27、);,当程序中遇到这个语句时,如果x的值大于或等于10,则将打印包含行号和文件名的错误信息,而且程序终止。,练习题1,给出以下程序的执行结果。#include void main()int i,j,k;for(i=1;i=6;i+)for(j=1;j=20-2*i;j+)cout;for(k=1;k=i;k+)cout i;cout endl;,上机实验题1,编写一个程序,将用户输入的由数字字符和非数字字符组成的字符串中的数字提取出来,例如输入asd123rt456,fg789,则产生的数字分别是123、456和789。,第2章 类和对象(一),类,类的声明,类是一种用户自定义的数据类型,声明
28、类的一般格式如下:,class 类名private:私有数据成员和成员函数;protected:保护数据成员和成员函数;public:公有数据成员和成员函数;各个成员函数的实现;,类界面,类实现,在声明类时有如下规则:,如果类的成员是变量,可以像声明变量一样声明它。如果类的成员是函数,一般是使用函数原型来声明它。如果类的成员是函数,它可以访问类中的任何成员数据成员和成员函数。也就是说,当声明类的成员函数时,定义的函数可以直接访问该类中任何成员而无需将其声明为参数,惟一的限制条件是在使用一个成员之前必须声明它。,类的组织形式,通常将类界面与类实现分离,将类界面部分存放在头文件(.h)中,将类实现
29、放在程序文件(.cpp)中,而使用类的程序放在另一个程序文件中,这样使整个程序更清晰。例如,声明类C的类界面的部分用c.h文件保存,类实现部分用c.cpp文件保存,而使用类C的部分用a.cpp保存,如图所示。,类的作用域,声明类时所使用的一对大括号()形成了类作用域。在类作用域中声明的标识符只在该类中具有可见性,并且其作用域与该标识符声明的次序无关。,类作用域包括了类中成员函数的作用域,即使该成员函数的实现放在类的外面也是如此。所以当成员函数的函数体中使用一个标识符时,编译系统首先在成员函数中寻找其声明,如果未找到则在该成员函数所在的类中寻找,如果还未找到,则在包含类作用域的更大作用域中作最后
30、寻找。,类的成员函数,类的成员函数用于实现某种操作,成员函数的定义体可以在类的声明体中,也可以在类的说明体外。,在类声明体中实现的函数是内联函数。在类声明体外实现的函数可以通过在函数声明和定义上分别加上inline来表示该函数是内联的,否则不是内联函数。,在类的声明体内定义成员函数的优点是使整个类集中于程序代码的同一位置上,不利的方面是增加了类声明的规模和复杂性,而且,内联的函数代码并不被相同类的对象所共享,因而增大了程序的内存开销。,类的访问权限,在类声明中,public、private和protected是关键字,称为成员访问限定符,它们分别表示公有、私有和保护的成员访问权限。,在C+中,
31、有关类的访问权限的其他规定如下:,在默认的情况下,一个类中所有的成员都是私有的。一旦给出了成员访问限定符(如public:),它后面的成员都具有这个成员访问权限(如后面的成员均为公有的),直到出现另一个成员访问限定符或类声明结束为止。,类与结构体类型的区别,从类的声明格式可以看出,类与结构体类型是非常相似的。C+中类是由结构体类型演化而来的,但对结构体类型进行了扩展。类的成员可以是数据成员或成员函数,结构体中的成员也可以是数据成员或函数成员。并且在结构体中,也可以使用关键词public、private、protected限定其成员的访问权限。,实际上,结构体只是类的一个特例。结构体与类惟一区别
32、在于:在类中,其成员的默认访问权限是私有的,而在结构体类型中,其成员的默认访问权限是公有的。,当只需要描述数据结构时,使用结构体较好。当需要描述数据又需要描述对数据的处理方法时使用类为好。,类的特点,类具有如下特点。,(1)类具有封装性(2)类具有安全性(3)类具有独立性与可维护性(4)类具有继承性(5)类具有多态性,对象,对象的定义格式,一旦声明了一个类,就可以用它作为数据类型来定义类对象(简称为对象)。在C+中,类对象也称为类变量或者类实例。,定义类对象的格式如下:,类名 对象名表;,其中,“类名”是待定的对象所属的类的名字,即所定义的对象是该类的对象。“对象名表”中可以有一个或多个对象名
33、,多个对象名之间用逗号分隔。在“对象名表”中,可以是一般的对象名,还可以是指向对象的指针变量名(即对象指针)、引用名(即对象引用)、对象数组名。,对象的数据成员访问方法,对象的数据成员的访问方式如下:,对象名.数据成员名,其中,“.”是一个运算符,该运算符的功能是表示对象的成员。,对象的成员函数调用方法,成员函数的调用是为了响应发送对象的消息。消息对应于从一个对象向另一个对象或者从一个函数向一个对象发送的成员函数调用。,调用对象的成员函数的方式如下:,对象名.成员名(参数表),对象的存储空间,实际上,C+只为每一个对象的数据成员分配内存空间,类中的所有成员函数只生成一个副本,而该类的每个对象执
34、行相同的函数成员副本。因此在描绘类图时,通常将显示类中所有的成员。然而在描绘类的对象图时,将只显示其数据成员。,实际上,类对象也像变量一样,可以定义全局对象或静态对象等,它们的存储空间类型和作用域与全局变量或静态变量是一样的。,对象的赋值运算,在定义了一个类的多个对象时,可以在对象之间进行赋值运算,也称为对象复制,假设定义了例2.1中类MyClass的两个对象sa1和sa2,它们的值如图所示。,构造函数,什么是构造函数,C+提供了利用类的构造函数来初始化类的数据成员。,构造函数具有如下性质:,构造函数的名字与类的名字相同。构造函数尽管是一个函数,但没有任何类型,即它既不属于返回值函数也不属于v
35、oid函数。类可以有多个构造函数。然而,一个类的所有构造函数的名字都相同。如果类有多个构造函数,则它们的参数是各不相同的。当类对象创建时,构造函数会自动地执行;由于它们没有类型,不能像其他函数那样进行调用。当类对象说明时调用哪一个构造函数取决于传递给它的参数类型。,调用构造函数,当定义类对象时,构造函数会自动执行。因为一个类可能会有包括默认构造函数在内的不止一种构造函数,下面讨论如何调用特定的构造函数。,1.调用默认构造函数,假设一个类包含有默认构造函数,调用默认构造函数的语法如下:,类名 类对象名;,2调用带参数的构造函数,假设一个类中包含有带参数的构造函数,调用这种带参数的构造函数的语法如
36、下:,类名 类对象名(参数表),其中,“参数表”中的参数可以是变量,也可以是表达式。,3.调用内置数据类型的构造函数,实际上,C+中内置的数据类型都可以看成是类,定义变量时除了可以使用“=”运算符赋初值外,还可以像定义类对象一样调用其构造函数给变量赋初值。,4.用new动态创建对象,可以用new运算符来动态地建立对象。用new运算符建立对象时,同样也要自动调用构造函数,以便完成对象数据成员的初始化。,5.用构造函数初始化对象的过程,构造函数初始化对象的过程,实际上就是对构造函数的调用过程。一般情况下按如下步骤进行:,(1)程序执行到定义对象语句时,系统为对象分配内存空间。(2)系统自动调用构造
37、函数,将实参传送给形参,执行构造函数体时,将形参值赋给对象的数据成员。完成数据成员的初始化工作。,重载构造函数,构造函数可以像普通函数一样被重载,C+根据声明中的参数个数和类型选择合适的构造函数。,复制构造函数,复制构造函数常用于将一个已知对象的数据成员复制给正在创建的另一个同类的对象。除此之外,复制构造函数可以像其他构造函数一样定义和使用。,复制构造函数的格式如下:,类名:复制构造函数(类名&引用名)或:类名:复制构造函数(const 类名&引用名),在以下3种情况下,复制构造函数都会自动被调用:,当说明一个类的对象时,使用另外一个对象来初始化(此时复制构造函数就如同其他构造函数一样使用)。
38、当一个函数返回值为类类型时。当一个类类型的实参传递给函数中的传值调用参数时,在这种情况下,复制构造函数决定了参数按照什么方式传递。,析构函数,什么是析构函数,同构造函数一样,析构函数也是类的特殊成员函数,不用调用便自动执行,而且析构函数的名字也与类的名字有关。,C+程序设计的一个原则是:由系统自动分配的内存空间由系统自动释放。,析构函数的性质,析构函数具有如下性质:,析构函数在类对象销毁时自动执行。一个类只能有一个析构函数,而且析构函数没有参数。析构函数的名字是“”加上类的名字(中间没有空格)。与构造函数一样,析构函数也没有任何类型,即不属于返回值函数也不属于void函数。它们不能像其他函数那
39、样被调用。,析构函数的调用,析构函数被系统自动调用时分为以下两种情况。,1.用类直接建立对象,在程序执行过程中,当遇到对象的生存期结束时,系统自动调用析构函数,然后回收为对象所分配的存储空间。,2.用new动态创建对象,对于用new运算符动态创建的对象,在产生对象时调用构造函数,只有用delete 释放对象时,才调用析构函数。若不使用delete 运算符来撤销动态生成的对象,程序结束时对象仍存在,并占用相应的存储空间,即系统不能自动撤销动态创建的对象。,一个对象的生存期,如同一个变量一样,对象从被创建到被释放为止的时间称为对象的生存期。一个对象的生存期如图所示。,对象浅复制与深复制,对象浅复制
40、,当两个对象之间进行复制时,若复制完成后,它们还共享某些资源(内存空间),其中一个对象的销毁会影响另一个对象,这种对象之间的复制称为对象浅复制。,对象深复制,当两个对象之间进行复制时,若复制完成后,它们不会共享任何资源(内存空间),其中一个对象的销毁不会影响另一个对象,这种对象之间的复制称为对象深复制。,静态成员,静态数据成员,静态数据成员是类中所有对象共享的成员,而不是某个对象的成员,也就是说静态数据成员的存储空间不是放在每个对象中,而是和成员函数一样放在类公共区中。所以有时将静态数据成员称为类变量。,因为静态数据成员不从属于任何一个具体对象,所以必须对它初始化,而且对它的初始化不能在构造函
41、数中进行。,静态数据成员的使用方法如下:,(1)静态数据成员的定义与一般数据成员相似,但前面要加上static关键词。当数据成员的一个副本就已经足够时,可使用static数据成员来节省存储空间。(2)静态数据成员的初始化与一般数据成员不同,静态数据成员初始化的格式如下:类型 类名:静态数据成员=值;(3)在引用静态数据成员时采用格式类名:静态数据成员,静态成员函数,静态成员函数与静态数据成员类似,也是从属于类,都是类的静态成员。只要类存在,静态成员函数就可以使用,静态成员函数的定义是在一般函数定义前加上static关键字。调用静态成员函数的格式如下:类名:静态成员函数名(参数表);,类成员指针
42、,类数据成员指针,类数据成员指针的定义格式如下:,类型 类名:*指针名,由于类不是在运行时存在的对象,因此在使用类数据成员指针时,需要首先指定类的一个数据成员,然后通过类的对象来引用指针所指向的成员。,类成员函数指针,指向类成员函数指针的定义格式如下:类型(类名:*指针名)(参数表)给类成员函数指针赋值的格式如下:指向函数的指针名=函数名程序中使用指向函数的指针调用函数的格式如下:(*指向函数的指针名)(实参表),应用实例,编写一个程序,设计一个满足如下要求的CDate类,用数据进行调试并输出结果:,(1)用日/月/年格式输出日期。(2)可进行日期加一天的操作。(3)设置日期。,练习题2,给出
43、以下程序的执行结果。,#include class Sample int x,y;public:Sample()x=y=0;Sample(int a,int b)x=a;y=b;Sample()if(x=y)cout x=y endl;else cout x!=y endl;void disp()cout x=x,y=y endl;void main()Sample s1(2,3);s1.disp();,上机实验题2,设计一个词典类Dic,其中包含若干单词信息,每个单词由英文单词及对应的中文含义组成。并含有单词增加和英汉翻译成员函数,通过查词典的方式将一段英语翻译成对应的汉语。,第3章 类和对
44、象(二),常对象和常对象成员,常对象,常对象是指对象常量,其一般定义格式如下:,类名 const 对象名;或者:const 类名 对象名;,使用常对象成员时需要注意以下几点:,在定义常对象时必须进行初始化。常对象的数据成员不能被更新。,常对象成员,常对象成员包括常成员函数和常数据成员。,1.常成员函数,使用const关键词声明的函数为常成员函数,常成员函数声明格式如下:,类型 函数名(参数表)const;,使用常对象成员时需要注意以下几点:,const是函数类型的一个组成部分,因此在实现部分也要带const关键词。常成员函数不更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
45、如果将一个对象说明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数。const关键词可以参与区分重载函数。例如,在类中有如下说明:“void print();void print()const;”,则这是对print的有效重载。,2.常数据成员,与一般数据相同,类的成员数据也可以是常量和常引用,使用const说明的数据成员称为常数据成员。如果在一个类中说明了n个常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,其一般格式如下:,构造函数(参数表):常数据成员1(参数1),常数据成员2(参数2),常数据成员n(参数n),其中,冒号后面是一个数据成员的初始化
46、列表,它包含一个初始化项,当有多个初始化项时,要用逗号分隔开。“参数1”“参数n”均为“参数表”中的某个参数。这样,在执行构造函数时自动将“常数据成员1”赋值为“参数1”的值,“常数据成员2”赋值为“参数2”的值,“常数据成员n”赋值为“参数n”的值。,类对象数组,对象数组是指一个数组元素都是对象的数组,也就是说,若某一个类有若干个对象,则可以把这一系列被创建的对象用一个数组来存放。,若要定义一个带有构造函数的类的对象数组,那么这个类必须含有一个不带参数的构造函数或带有缺省参数的构造函数,这是因为对象数组被创建时无法对构造函数传递参数,只能调用默认构造函数来初始化(数组中的)每一个类对象。,假
47、设定义了包含100个对象的数组,那么给每一个类对象指定不同的构造函数是不现实的。,假如己声明了一个类MyClass,则有以下语句:,MyClass s100;,子对象,has-a关系,如果类A的声明中将类B的对象作为数据成员,也就是说,类A有一个类B的对象(即子对象),则称类A和类B之间是一种has-a关系,通常用图3.1表示,从类B的结点画一条线到类A的结点,并在类A结点的一端标记一个实心小圆。has-a关系是一种组合关系,下图表示类A对象是由类B对象组成的。,两个类has-a关系的图示,子对象构造函数的设计和执行次序,当含有子对象的类存在构造函数时,情况就比较复杂,特别要注意构造函数的调用
48、次序。设类A含有子对象obj,该子对象对应的类为B,如下所示:,class B;class A B obj;/obj是类B的对象,是类A的子对象;,在一个类的定义中,若定义有n个对象成员,则其构造函数的一般格式为:,类名:类名(形参表):对象名1(实参表1),对象名2(实参表2),对象名n(实参表n)构造函数体,其中:“对象名1(实参表1),对象名2(实参表2),对象名n(实参表n)”称为成员初始化表。对该构造函数的几点说明如下:,形参必须带有类型声明,而实参是可计算值的表达式。对象成员构造函数的调用顺序取决于这些对象成员在类中的声明顺序,而与它们在成员初始化表的位置无关。建立类的对象时,先调
49、用各个对象成员的构造函数,初始化相应的对象成员,然后才执行类的构造函数,初始化类中其他成员。析构函数的调用顺序与构造函数正好相反。,子对象析构函数的设计和执行次序,同样,当含有子对象的类存在析构函数时,特别要注意析构函数的调用次序。在含有子对象的类A中,设计析构函数如下:,A()函数体;,其执行次序是:先执行函数体,再以子对象在类A中说明的相反次序调用各类的析构函数。,前向引用声明,C+的类应当先声明,然后再使用。但是在处理相对复杂的问题时,很可能遇到两个类相互引用的情况,这时候,就必然要有一个类在定义之前就被引用,解决这一问题的方法是使用前向引用声明。,前向引用声明是在引用未定义的类之前对该
50、类进行声明,它只是为程序引入一个代表该类的标识符,类的具体定义可以在程序的其他地方。,嵌套类,可以在一个类声明中声明其他类,这种在其他类中声明的类称为嵌套类。其层次结构如下:,class OuterClasspublic:private:class InnerClass;,嵌套类可以是公有的,也可以是私有的。,由于嵌套类是在外层类的范围内声明的,有时候嵌套类(如上面所示的类InnerClass)也可能在外层类之外使用。如果嵌套类是公有的,那么嵌套类也可以在外层类之外作为一种数据类型使用,但此时嵌套类的类型应该是OuterClass:InnerClass。,局部类,一个类的声明还可以是在某一个函