关于使用课件的几点说明 1. 本人认为课件最好是讲课老师自己作,这里只提供一此参考资料,供讲课老师备课时参考。我所提供的内容尽量多些,不需要的可以删减。 2. 该课件是按教材章节顺序编写,基本上包含教材中的全部内容。在实际学中,讲课老师可以参照该课件适应增删。 3. 课件中只给出纲要,没有具体例子,讲课时可由老师举一些或者书中例子或者书外例子。给老师一些灵活性及选择性。 4. 每章讲完后老师可根据情况选择一些作业题和上机指导题。这里没具体给出。 5. 建议不要把书中程序作在课件里,需要时让同学们看书。补充的程序可放在课件中字体大些让同学们看清。 6. 作课件时要将背景色与前景色的反差大些,否则远处看不清。 7. 根据学生情况,如果学生对C语言有所了解,则前4章可以少讲些。只要复习一下C语言的内容就可以了。 作者 1 第1章 C++语言概述 1.1 面向对象程序设计的有关概念 1.1.1 面向对象方法的由来和发展 在面向对象方法出现之前,人们采用的是面向过程的方法。面向过程方法是一种传统的求解问题的方法。 面向对象方法是求解问题的一种新方法,它把求解问题中客观存在的事物看作各自不同的对象,再把具有相同特性的一些对象归属为一个类 面向对象方法是计算机科学发展的要求。这种方法满足了人们对信息的需求量越来越大,对软件开发的规模也越来越大,对软件可靠性和代码的重用性的要求越来越高的客观需要。 1.1.2 面向对象的基本概念 1.对象 对象是现实世界中客观存在的某种事物。对象是一种相对独立的实体,它具有静态特性和动态特性,通常通过一组数据来描述对象的静态特性,使用一组行为或功能来表示对象的动态特性。 2.类 类是人们对于客观事物的高度抽象。 面向对象方法中的类是一种类型,它是具有相同属性和行为的对象的集合。类是具有相同属性和行为的若干对象的模板。类为属于该类的全部对象提供了抽象的描述,这种描述包括了属性和行为两大部分。 3.封装 封装是指把对象的属性和行为结合成一个独立的单位,又称为封装体。 封装体具有独立性和隐藏性。 一个封装体与外部联系只能通过有限的接口。 4.继承 继承是面向对象方法提高重用性的重要措施,继承表现了特殊类与一般类之间的关系。继承的重要性就在于它大大地简化了对于客观事物的描述。 5.多态性 多态性指的是一种行为对应着多种不同的实现。在同一个类中,同一种行为可对应着不同的实现。 1.2 C++语言的特点 1.2.1 C++语言是面向对象的程序设计语言 2 1.支持封装性 C++语言允许使用类和对象。类是支持数据封装的工具,对象是数据封装的实现。 类中成员有不同的访问权限。 2.支持继承性 C++语言支持面向对象方法中的继承性,它不仅支持单重继承,而且支持多重继承。 继承和封装是衡量一种语言是否是面向对象的程序设计语言的两个重要标准。 3.支持多态性 多态性是在继承性基础上的面向对象方法中的重要特性之一。 ① 支持函数重载和运算符重载。 ② 支持动态联编。 1.2.2 C++语言继承了C语言 C++语言与C语言兼容,C语言是C++语言的一个子集。 C++语言具有C语言的简练明了的风格,同时还保留某些C语言的面向过程的特性。实际上,C++语言是一种不完全的面向对象的程序设计语言。 1.2.3 C++语言对C语言进行了改进 ① C++语言中规定函数定义时必须指出类型。 ② C++语言规定函数说明必须使用原型说明,不得用简单说明。 ③ C++语言规定凡是从高类型向低类型转换时都需加强制转换。 ④ C++语言中符号常量建议使用const关键字来定义。 ⑤ C++语言中引进了内联函数。 ⑥ C++语言允许设置函数参数的默认值。 ⑦ C++语言引进了函数重载和运算符重载。 ⑧ C++语言引进了引用概念,使用引用作函数的参数和返回值。 ⑨ C++语言提供了与C语言不同的I/O流类库,方便了输入/输出操作。 ⑩ C++语言为方便操作还采取了其他措施。 1.3 C++语言的词法皮词法规则 1.3.1 C++语言的字符集 C++语言字符集同于C语言字符集。 1.大小写英文字母 2.数字字符 3.其他字符 1.3.2 单词及其词法规则 3 1.标识符 标识符是用来命名程序中一些实体的一种单词。使用标识符来命名的有变量名、函数名、类名、对象名、常量名、类型名、语句标号名、宏名等。 C++语言规定,标识符是由大小写字母、数字字符和下划线符组成的,并且以字母或下划线开头的字符集合。 2.关键字 关键字是系统已经定义过的标识符,它在程序中已有了特定的含义。下列是C++语言中常用的关键字。 auto break continue default enum explicit goto if new operator return short struct switch virtual void case delete char do class const double else for friend long mutable public register static static_cast union unsigned extern float inline int private protected signed sizeof this typedef while 3.运算符 运算符是一种用于进行某种操作的单词。运算符是由1个或多个合法字符组成的。 C++语言的运算符除包含了C语言的全部运算符外,还增加了5个新的运算符。 4.分隔符 C++语言中常用的分隔符与C语言中的相同,它们包括几种。 ① 空格符:用做单词之间的分隔符。 ② 逗号符:用做变量名之间或对象名之间的分隔符,还可以用做函数表中参数之间的分隔符。 ③ 分号符:专用在for循环语句中关键字for后边括号内的3个表达式之间的分隔符。 ④ 冒号符:仅用做语句标号和语句之间以及开关语句中case<整常型表达式>与语句序列之间的分隔符。 另外,单撇号、双撇号和花括号用于某些实体的定界符。 5.常量 C++语言的常量种类与C语言的常量种类相同,有数字常量(包括整型常量和浮点型常量)、字符常量、字符串常量和枚举常量。 C++语言中使用关键字const来定义符号常量。 6.注释符 C++语言中保留了C语言的注释符(/*和*/),并且还使用一种新的行注释符。 行注释符(//)用来注释从该注释符以后的该行信息为注释信息。 1.4 C++程序在结构上的特点 4 1.4.1 C++程序举例 见书中例1.1。 1.4.2 C++程序结构上的特点 ① C++语言程序是由若干个类和函数组成的。这些类和函数可以放在一个文件中,也可以放在多个文件中。 ② C++语言程序中的函数有两个种类,一个种类是类体内的成员函数,另一个种类是类体外的一般函数。 ③ C++程序中有且仅有一个主函数main()。C++程序是从主函数开始执行的。 ④ C++程序中的函数都是由函数头和函数体构成的,函数体由若干条语句组成的;函数头中包括函数名、函数类型和函数参数。 ⑤ C++语言程序与C语言程序一样,可以使用预处理命令,也可以使用注释信息。 1.4.3 C++程序的书写格式 C++程序分C语言程序一样,可读性较差,要注意书写格式。通常遵循下列约定: 一行写一条语句。短语句可以一行写多个,长语句也可以写多行。 采用适当的缩格书写方式很重要。 大括号的使用要统一。 见书中例1.2和例1.3。 1.5 C++程序的实现 1.5.1 C++程序的编辑、编译和运行 1.编辑 编辑是将编写好的C++语言源程序通过输入设备录入到计算机中,生成磁盘文件加以保存。 2.编译 整个编译过程可分为如下3个子过程。 ① 预处理过程。 ② 编译过程。 ③ 连接过程。 3.运行 运行可执行文件后,在屏幕上输出显示其运行结果。 1.5.2 C++程序实现举例 学会一种C++语言的实现方法,本书介绍了VC++ 6.0的基本用法。 1.单文件程序的实现方法 见例1.4 2.多文件程序的实现方法 见例1.5 5 作业题和上机练习题 6 第2章 数据类型和表达式 2.1 基本数据类型 基本数据类型有下述几种类型。 整型 字符型 浮点型 空值型 布尔型 可用的修饰符有以下4种。 signed 表示有符号型,常被省略。 unsigned 表示无符号型。 long 表示长型。 short 表示短型。 表2.1列出了各种基本数据类型的类型名、数据宽度(即在内存中占的字节数)和取值范围。这里给出的取值范围和数据宽度是在32位机中的情况,对于16位机应适当调整。 表2.1 类 型 名 char signed char unsigned char short [int] signed short [int] unsigned short [int] int signed [int] unsigned [int] long [int] signed long[int] unsigned long [int] float double long double bool C++语言的基本数据类型 说 明 字符型 有符号字符型 无符号字符型 短整型 有符号短整型 无符号短整型 整型 有符号整型 无符号整型 长整型 有符号长整型 无符号长整型 单精度浮点型 双精度浮点型 长双精度浮点型 布尔型 字 宽 1 1 1 2 2 2 4 4 4 4 4 4 4 8 16 1 范 围 −128~127 −128~127 0~255 −32768~32767 −32768~32767 0~65535 −2147483648~2147483647 −2147483648~2147483647 0~4294967295 −2147483648~2147483647 −2147483648~2147483647 0~4294967295 约6位有效数字 约12位有效数字 约15位有效数字 true,false 2.2 常量和变量 2.2.1 常量 1.整型常量 整型常量可以用十进制、八进制和十六进制表示。 (1)十进制整型常量 (2)八进制整型常量 7 (3)十六进制整型常量 2.浮点型常量 浮点型常量的表示形式有如下两种。 ① 小数表示形式,又称一般形式。具体格式如下: <整数部分>.<小数部分> ② 指数表示形式,又称科学表示法。具体格式如下: <整数部分>.<小数部分>e<指数部分> 浮点型常量分单精度、双精度和长双精度3类。 3.字符型常量 字符型常量是用一对单撇号括起一个字符来表示的。字符型常量表示方法有如下两种。 ① 用一对单撇号括起一个图形符号。 ② 用一对双撇号括起反斜线符加上字符的ASCII码值。这里可使用下述两种形式: '\\0ddd' 和 '\\xhh' 表2.1 符 号 \\a \\n \\r \ \\b C++语言中常用的转义字符 含 义 鸣铃 换行符 回车符 水平制表符(Tab键) 退格符(Backspace键) 符 号 \\\\ \\' \\\" \\0 含 义 反斜线 单撇号 双撇号 空字符 4.字符串常量 字符串常量是由一对双撇号括起的字符序列。 字符串常量可由任何字符组成,包含空格符、转义字符和其他字符,也包含汉字。 字符串都有一个结束符,用来标识字符串的结束。该结束符是'\\0',即ASCII码值为0的空字符。 字符常量与字符串常量是有区别的。 5.枚举常量 枚举是一种构造数据类型,具有这种类型的量称为枚举量。枚举是若干有名字的整型常量的集合,这些整型常量组成了枚举表,枚举表中的每一项称为枚举符,枚举符实际上是一个具有名字的常量。枚举量便是该枚举表中的一个枚举符,它实际上是一个int型常量,故称为枚举常量。 (1)枚举类型和枚举常量的定义 定义枚举常量之前必须先定义枚举类型。枚举类型的定义格式如下: enum<枚举名>{<枚举表>}; 例如, enum day {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; 枚举符的值可以在定义时被显式赋值。某个枚举符被显式赋值后,它的值就是被赋的值。没有被赋值的枚举符的值是它前一个的值加1。 枚举量的定义格式如下: 8 enum <枚举名> <枚举量名表>; 或者在定义枚举类型的同时定义枚举量: enum <枚举名>{<枚举表>} <枚举量名表>; (2)枚举量的值 枚举量的值被限定为该枚举类型的枚举表中的某个枚举符。枚举量是通过对应枚举表中的枚举符给它赋值的。 (3)使用枚举量的好处 ① 枚举量的取值范围受到一定的限制,这将增加数据的安全性。 ② 枚举量使用具有整型值的枚举符,可增加程序的可读性。 ③ 被说明的枚举量系统进行类型检查,这样也会增加数据的安全性。 6. 符号常量 在C++程序中,所出现的常量通常使用符号常量来表示。符号常量就是使用一个标识符来表示某个常量值。使用符号常量不仅可增加程序的可读性,而且为修改常量值带来极大的方便。 在C++语言中,定义常量使用常类型说明符const。具体定义格式如下: const <类型说明符> <常量名>=<常量值>; 或者 <类型说明符> const <常量名>=<常量值>; 定义常量时应做到如下几点: 使用常类型说明符const; 确定常量名,同标识符; 指定常量类型; 给出常量值。 由于C++语言与C语言兼容,C语言中使用宏定义来定义符号常量在C++语言中也可使用。 见书中例2.1 2.2.2 变量 1.变量的名字 给变量起名字时应注意遵守下列规则。 ① 变量名同标识符,组成变量名中的字母大小写是有区别的。 ② 命名变量时应尽量做到“见名知意”,这样有助于记忆,并增加可读性。 ③ 系统中使用的关键字、库中的类名和函数名等不能用作变量名。 ④ 变量名长度没有限制,但不宜过长,通常不超过31个字符。 2.变量的类型 变量的类型包括数据类型和存储类。 (1)数据类型 数据类型分为基本数据类型和构造数据类型两种。 ① 基本数据类型 基本数据类型如前所述。 ② 构造数据类型 9 构造数据类型又称用户自定义数据类型。这种数据类型主要包括数组、结构、联合和类等。 (2)存储类 存储类是指变量被存放的地方,存放的地方不同决定其寿命和作用域不同。 变量的存储类有如下4种: ① 自动类(auto); ② 寄存器类(register); ③ 外部类(exfern); ④ 静态类(static)包括内部静态和外部静态。 各种不同存储类的特点如下。 ① 从寿命上讲,自动类和寄存器类的寿命是短的。外部类和静态类的寿命是长的。 ② 从作用域上讲,自动类,寄存器类和内部静态类的作用域是在定义或说明它们的函数体内或分程序内。外部类的作用域是整个程序,包括该程序的所有文件。外部静态类的作用域是定义或说明它的文件内,并从定义时开始。 ③ 任何一个变量在定义或说明时都要给出存储类。 ④ 静态类和外部类变量在定义或说明中不给初值时,它们具有默认值;自动类和寄存器类变量在定义或说明中不给初值时,它们具有无意义值。 3.变量的值 (1)变量的两个值 变量被定义后,它就应该具有两个值。 ① 变量本身值。该值是在定义变量时获取的,该值可以被改变。 ② 变量地址值。该值是由系统分配内存空间时确定的,它是一个内存地址值,该值是不能改变的。 (2)变量值获取的两种方法 ① 定义或说明变量时变量可获取初值、默认值或无意义值。 ② 通过赋值方法改变变量的值。 4. 变量的定义格式 定义一个变量其格式如下: <类型> <变量名> = <初值>,<变量名>… 一个变量被定义后,它就有确定的作用域和寿命,并且具有确定的地址值,其变量值或者为某个初值,或者为默认值,或者为无意义值。 定义变量时必须给出的有: ① 变量名,同标识符; ② 变量数据类型; ③ 变量存储类,省略的是自动类,不给出的是外部类。 另外,定义或说明变量时可以给变量赋初值,也可以不给变量赋初值。 见本书例2.2 2.3 数组 2.3.1 数组的定义格式 10 数组是C++语言中的一种构造的数组类型。数组类型的具体描述是数目固定、类型相同的若干个变量的有序集合。 数组的定义格式如下: <类型说明符> <数组名> [<大小1>] [<大小2>]… (1)一维数组 一维数组的定义格式如下: <类型说明符> <数组名> [<大小>] 例如, int a[5]; (2)二维数组 二维数组定义格式如下: <类型说明符> <数组名> [<大小1>] [<大小2>] 例如, int bb [3] [5]; (3)三维数组 三维数组的定义格式如下: <类型说明符> <数组名> [<大小1>] [<大小2>] [<大小3>] 例如, int aaa[2] [3] [5]; 2.3.2 数组的赋值 1. 数组元素表示 数组元素表示格式如下: <数组名> [<下标1>] [<下标2>]… 数组元素被存放在机器的内存中是按顺序存放的。 2.数组元素的初始化 数组初始化是使用初始值表进行的。 (1)一维数组的初始化 一维数组初始化使用一重初始值表,其格式如下: <类型说明符> <数组名> [<大小>]={<数据项表>}; 在使用初始值表给数组元素初始化时,要求初始值表中数据项的个数小于或等于数组元素的个数。数组在赋初值时是不会越界的。 在定义一维数组并给它进行初始化时,可以省略数组的大小。 (2)二维数组的初始化 二维数组的初始化使用二重初始值表,其格式如下: <类型说明符> <数组名> [<大小1>] [<大小2>]={{<数据项>}, {<数据项>}, …}; 同样,在定义二维数组并同时初始化时,二维数组中第1维的大小可以省略,但是第2维的大小不能省略。例如, (3)三维数组的初始化 三维数组初始化可使用三重初始值表,其格式如下: <类型说明符> <数组名> [<大小1>] [<大小2>] [<大小3>]= {{{<数据项>}, {<数据项>}, …}, {{<数据项>}, {<数据项>}, …}, …}; 11 在给三维数组的初始化的定义中也可以不指定一维的大小,由系统根据初始值表中的数据项来确定其大小。 3.数组元素的赋值 数组的赋值实际上是给数组的各个元素赋值。给数组元素赋值的方法是通过赋值表达式进行的。 2.3.3 字符数组 1. 字符数组的定义格式 字符数组的定义格式与一般数组的定义格式相同,所不同的是数据类型说明符使用char,字符数组也分一维、二维和三维字符数组。 2. 字符数组的初始化 在定义字符数组时也可以给该数组进行初始化,初始化的方法以下有两种。 (1)使用初始值表 使用初始值表给定义的字符数组初始化时,初始值表中的数据项应该是字符常量,或者是字符常量表达式。 (2)使用字符串常量 使用字符串常量给定义的字符数组初始化时,该字符数组中存放被赋初值的字符串。 3. 字符数组的赋值 字符数组的赋值就是使用赋值表达式语句给字符数组中的每个数组元素赋值。每个数组元素的值应是一个字符常量。通过采用如下两种方法为字符数组赋值。 ① 使用赋值表达式语句逐个对每个数组元素赋值。 ② 使用循环语句给字符数组的各个元素赋值。 2.4 指针和引用 2.4.1 指针 1. 什么是指针 指针是一种特殊的变量。 指针的名字同标识符。 指针的值是用来存放某个变量或对象的地址值的。 指针的类型是该指针所指向的变量或对象的类型。一个指针存放了哪个变量或对象的地址值,则该指针就指向哪个变量或对象。 总之,对指针的认识简单描述如下: 内 存 指针是一种用来存放某个变量或对象地址值的变量;一个指针存M 放了哪个变量或对象的地址值,则该指针便指向哪个变量。指针的类地址值 a 5 1000H 型是该指针所指向的变量的类型。 M 下面通过图示方法介绍指针与它所指向的变量的关系。 pa 1000H 2000H M 2. 指针的定义格式 图4.1 指针的定义格式如下: <类型> * <指针名> [=<初值>]; 12 下面列举一些常用的不同类型的指针的定义格式。 double *pd; // pd是一个指向double型变量的指针 // pc是一个指向char型变量的指针 // pfl是一个指向float型变量的指针 // pa是一个指向一维数组的指针,该数组是具有3个元素的int 型数组。 // pfu是一个指向函数的指针,该函数是一个无参数的int型函数。 // pp是一个指向一级指针的指针,即为二级指针。 char *pc; float *pfl; int (*pa)[3]; int (*pfu)(); int **pp; 3. 指针的赋值运算 指针在定义或说明时可以被赋初值,指针也可以被赋值。 指针赋值的规则如下: 指针被赋的值应该是变量或对象的地址值,并且要求类型相同、级别一致。 在给指针赋值时除了要遵循上述规则外,还应注意如下事项。 ① 指针定义后在没有确定值前绝对不能使用。 ② 暂时不用的指针为了安全起见,可先给它赋值为0,即为一空指针。 ③ 可将一个已知指针赋值给相同类型的另一个指针。 ④ 指针可以使用malloc()函数赋值。例如, int *p; p = (int*) malloc(sizeof(int)); 4. 指针的运算 (1) 赋值运算 同前 (2) 指针可加减整型数运算 指针可以被加上或减去一个int型数,包括加1或减1运算。 (3) 指针的比较运算和相减运算 在一定条件下,两个指针可以进行比较,也可以进行相减运算。 2.4.2 指针和数组 1. 指针可表示数组元素 (1) 一维数组元素的指针表示 C++语言规定数组名是一个常量指针,该指针的值是该数组首元素的地址值。 假定一维数组a定义如下: int a[5]; 该数组的下标表示为a[i],i=0~4。 该数组第i个数组元素的值表示为: *(a+i), 其中,i=0~4。 (2) 二维数组元素的指针表示 假定b是一个二维数组,定义如下: int b[3] [5]; 该数组元素的下标表示为b[i][j],其中,i=0~2,j=0~4。 13 二维数组元素用一级指针表示为: *(&b[0][0]+5*i+j) 二维数组可以看作是由行数组和列数组组成的,行数组和列数组都是一维数组。 二维数组元素中行、列数组都用指针表示的形式如下: *(*(b+i)+j) 当行数组用指针,列数组用下标表示的形式如下: (*(a+i))[j] 当行数组用下标,列数组用指针表示的形式如下: *(a[i]+j) 二维数组元素的地址值表示有如下几种形式: &b[i][j], b[i]+j, *(b+i)+j, &b[0]+5*i+j, &(*(b+i))[j] 二维数组的行地址表示有如下几种形式: a+i, &a[i] 二维数组的行地址是一个二级指针的地址值,它的值就是该行首列元素的地址值。 (3) 三维数组元素的指针表示 假定三维数组c定义如下: int c[2] [3] [4]; 三维数组元素都用下标表示为 c[i][j][k],其中,i=0~1, j=0~2, k=0~3 三维数组元素都用指针表示为 *(*(*(c+i)+j)+k) 三维数组元素中二维用指针一维用下标表示为 (*(*cc+i)+j) [k] *((*(c+i))[j]+k *(*(c[i]+j)+k) 三维数组元素中一维用指针二维用下标表示为 (*(c+i))[j] [k] (*(c[i]+j)) [k] *(c[i][j]+k) 还有一种是按三维数组在内存中存放顺序用数组的首地址表示如下: *(&c[0][0][0]+3*4*i+4*j+k) 见例2.1至例2.5。 2. 指针数组和指向数组的指针 (1) 指针数组 数组元素为指针的数组称为指针数组。 格式如下: <类型> * <指针数组名>[<大小>] 见书中例2.5。 (2) 指向数组的指针 指向数组的指针可指向一维数组或二维数组,它们分别是二级指针或三级指针。 指向一维数组指针格式如下: <类型> (*<指针名>)[<大小>] 14 见书中例2.6。 2.4.3 字符指针 1.字符指针 字符指针是指向字符串的指针。 字符指针指向字符串的首字符。由于字符指针名是变量指针,要比字符数组名(常量指针)操作起来更加方便。 字符数组只能在定义或说明时用字符串对它进行初始化,而字符指针不仅可用字符串对它进行初始化,而且还可以用字符串对它赋值。 见书中例2.7。 2. 字符指针数组 字符指针数组是指数组元素为字符指针的数组。它可用来存放若干个字符串。 见书中例2.8。 2.4.3 引用 1.引用的概念 引用是某个变量或对象的别名。引用不是变量,引用不占用内存空间。在建立引用时要用某个变量或对象对它初始化,于是引用便绑定在用来给它初始化的那个变量或对象上。 当创建某个变量的引用后,引用的值就是被引用的变量值,引用的地址值也是被引用的变量的地址值。 引用的创建格式如下: <类型> & <引用名> = <变量名/对象名> 见书中侧2.9。 3. 引用的应用 引用在C++程序中主要用作函数参数和返回值。后边章节中举例。 2.5 运算符 C++语言中除了包含有C语言的全部运算符外,它本身还有如下所示的5个运算符。 (1)作用域运算符::。 (2)使用对象或指向对象的指针通过指向类的成员的指针表示类的成员的运算符·*和−>*。 (3)创建堆对象和堆对象数组的运算符new,释放堆对象和堆对象数组的运算符delete。 2.5.1 运算符的种类和功能 1.算术运算符 单目:−(求负),++(增1),−−(减1) 双目:+(求和),−(求差),*(求积),/(求商),%(求余)。 说明: ①增1、减1运算符只能作用于变量,不能作用于常量和表达式。增1、减1运算符可 15 以有前缀运算和后缀运算。前缀运算时,表达式值是变量增1或减1后的值;后缀运算时,表达式值是变量增1或减1前的值。无论前缀运算还是后缀运算变量值都被增1或减1。 ② 增1、减1运算符具有副作用。 2.关系运算符 双目:>(大于),<(小于),>=(大于等于),<=(小于等于),= =(等于),!=(不等于)。 3.逻辑表达式 单目:!(逻辑求反) 双目:&&(逻辑与),||(逻辑或) 4.位操作运算符 (1)逻辑位运算符 单目:~(按位求反) 双目:&(按位与),^(按位异或),|(按位或)。 (2)移位运算符 双目:<<(左移),>>(右移) 5.赋值运算符 双目:=(基本赋值),+=(加赋值),− =(减赋值),*=(乘赋值),/=(除赋值),%=(求余赋值),<<=(左移赋值),>>=(右移赋值),&=(按位与赋值),|=(按位或赋值),^=(按位异或赋值)。 说明: ①赋值运算符具有副作用。 ② 赋值运算符的结合性是由右至左的。 6.其他运算符 (1)三目运算符 三目:?: 格式如下: d1?d2:d3 (2)逗号运算符 双目:, 逗号运算符的优先级最低。 (3)字节数运算符 单目:sizeof 说明:该运算符是用求得某种类型或某个变量在内存中存放时所占的内存字节数。其格式如下: sizeof(<类型说明符>/<变量名>) (4)强制类型运算符 单目:(<类型说明符>) 说明:该运算符用来将一个表达式的类型强制为某种指定的数据类型,其格式如下: (<类型说明符>) <表达式> 或者 <类型说明符>(<表达式>) (5)取地址和取内容运算符 16 单目:&(取地址),*(取内容) 这是两个与指针运算相关的运算符。 (6)成员选择运算符 用来表示复杂数据类型成员的两个运算符:·和−>。这两个运算符将在第7、8章中介绍。 (7)括号运算符 圆括号运算符()是用来改变运算符优先级的。规定在表达式中先作括号内的运算符,多层括号时,先作其内层括号,后作外层括号。 方括号运算符[ ]用来表示数组的元素,方括号内是一个下标表达式。 2.5.2 运算符的优先级和结合性 1.运算符的优先级 C++常用运算符的功能、优先级和结合性如表2.3所示。 表2.3 优 先 级 C++常用运算符的功能、优先级和结合性 运 算 符 ( ) :: [ ] ·,−> ·*,−>* ++,− − & * ! ~ +,− ( ) sizeof new,delete *,/,% +,− <<,>> <,<=,>,>= ==,!= & ^ | && || ?: =,+=,− =,*=, 功 能 说 明 改变优先级 作用域运算符 数组下标 成员选择 成员指针选择 增1、减1运算符 取地址 取内容 逻辑求反 按位求反 取正数,取负数 强制类型 取所占内存字节数 动态存储分配 乘法,除法,取余 加法,减法 左移位,右移位 小于,小于等于,大于,大于等于 相等,不等 按位与 按位异或 按位或 逻辑与 逻辑或 三目运算符 从右到左 从左至右 结 合 性 1 从左至右 2 从右至左 3 4 5 6 7 8 9 10 11 12 13 14 /=,%=,&=,^=, |=,<<=,>>= 赋值运算符 15 , 逗号运算符 从左至右 2. 运算符的结合性 在优先级相同的情况下,表达式中操作数的计算顺序取决于结合性。 运算符的结合性有两种,一种是从左到右,另一种是从右到左,它们是单目、三目和赋值运算符。详见表2.3。 17 2.6 表达式 2.6.1 表达式的种类 共6种 2.6.2 表达式的值和类型 (1)求表达式值的几个步骤 首先,确定表达式中运算符的功能。 其次,确定操作数的计算顺序,先由优先级决定运算顺序。在优先级相同的情况下,由结合性决定计算顺序。 (2)确定表达式类型的方法 ① 算术表达式的类型取决于组成该表达式中各个操作数类型高的类型。 ② 关系表达式的类型是逻辑型。在有些编译系统中,真用1表示,假用0表示。 ③ 逻辑表达式的类型与关系表达式类型相同。 ④ 赋值表达式的类型由赋值表达式左值的变量类型决定。 ⑤ 条件表达式的类型取决于表达式中冒号前后两个操作数中类型高的类型。 ⑥ 逗号表达式的类型是组成该表达式的最右边操作数的类型。 表达式求值举例 1.算术表达式 由算术运算符和位操作运算符组成的表达式都是算术表达式。 见书中例2.10,例2.11。 2.关系表达式 由关系运算符组成的表达式是关系表达式。 见书中例2.12。 3.逻辑表达式 逻辑表达式是由逻辑运算符与操作数组成的式子。 见书中例2.13。 4.条件表达式 使用三目运算符组成的表达式称条件表达式,因为它具有简单的条件语句的功能。 见书中例2.14。 5.赋值表达式 由赋值运算符组成的表达式称为赋值表达式。 见书中例2.15。 6.逗号表达式 18 由逗号运算符将若干个表达式连成一个逗号表达式。 见书中例2.16。 2.6.3 表达式中的类型转换 1. 隐含转换 自动转换都是隐式的保值转换。这种转换的原则是由低类型转换为高类型的转换。 关于各种类型的高低顺序如下所示: int→unsigned→long→double→long double ↑ ↑ short float char 2. 强制转换 (1) 显式强制转换 显式强制转换是通过强制类型转换运算符来实现的。 (2) 隐式强制转换 ① 赋值表达式中,右值表达式的类型隐式转换为左值变量的类型。 ② 在被调用函数带有返回值时,将return后面的表达式类型隐式强制转换为函数的类型。 见书中例2.17 2.7 类型定义 1.类型定义语句的格式 类型定义语句格式如下: typedef <已有类型名> <新类型名表>; 2.使用类型定义语句时应注意的事项 使用类型定义语句定义新类型时应注意如下事项。 ① 通常为了将新定义的类型与系统已有类型加以区别,习惯于将新定义的类型名用大写字母。 ② 类型定义可以嵌套。 3.使用类型定义的好处 使用类型定义有如下几点好处。 ① 可增加所定义的变量的信息,改善程序的可读性。 ② 可以将复杂类型定义为简单类型,从而达到书写简练的目的。 ③ 可提高数据的安全性。 见书中例2.18。 2.8 结构和联合 2.8.1 结构 19 1. 结构和结构变量的定义 结构是一种构造数据类型,具有这种数据类型的变量称为结构变量。 结构类型定义格式如下: struct <结构名> { <若干成员说明> }; 定义结构变量的格式如下: struct <结构名> <结构变量名表>; 2. 结构变量成员的表示和赋值 结构变量的成员表示规则如下: 一般结构变量的成员用运算符.表示。 指向结构变量的指针的成员用运算符->表示。 结构数组元素的成员表示用运算符.。 结构变量可以被赋初值,也可以被赋值。 给指向结构变量的指针初始化可以用相同结构类型的结构变量的地址值或用存储分配函数malloc()。 结构变量的赋值规则如下。 可以将一个结构变量的值赋给另一个相同结构类型的结构变量。 结构变量的运算主要是该结构变量的成员的运算。结构变量成员的运算取决于该成员的类型。结构变量整体运算只有赋值运算。 见书中例7.19。 3. 结构变量在程序中的应用 结构变量和指向结构变量的指针在程序中通常作为函数的参数和函数的返回值。结构变量作函数参数实现传值调用,其调用效率较低,指向结构变量的指针作函数参数实现传址调用,其调用效率较高。 见书中例7.20。 2.8.2 联合 联合类型与结构类型在形式上有许多相似之处。除了使用的关键字不同,定义联合时用union,定义格式上,成员表示上基本相同。联合与结构的本质区别在于结构成员是异址的,联合成员是共址的,这一区别决定了联合的使用不像结构那么多,同时也决定的应用也很局限。 见书中侧2.21。 作业题和上机练习题 20 第3章 预处理和语句 3.1 预处理功能 3.1.1 文件包含命令 文件包含命令的格式如下: #include <<文件名>> 或者 #include \"<文件名>\" 使用尖括号引用要包含的文件名时,所包含的文件被系统存放在指定的目录下,;使用双撇号引用要包含的文件名时,系统先到当前目录下查找,再到相连的目录下查找,最后到系统所指定的目录下查找。 使用文件包含命令时,应注意如下事项。 ① 系统提供的被包含文件,选用尖括号的包含格式;用户定义的被包含文件,选用双撇号的包含格式。 ② 一条文件包含命令只能包含一个文件,若有多个被包含文件可使用多条文件包含命令。 ③ 定义的被包含文件中还可以使用文件包含命令。 ④ 为了提高被包含文件的利用效率,定义被包含文件时应尽量短小。 ⑤ 文件包含命令最好放在程序头。 3.1.2 条件编译命令 该命令的作用是对源程序代码有条件的进行编译。 格式一: #ifdef <标识符> <程序段1> #else <程序段2> #endif 格式二: #ifndef <标识符> <程序段1> #else <程序段2> #endif 格式三: #ifdef <常量表达式1> <程序段1> #else <程序段2> #endif 见书中例3.1。 21 3.1.3 宏定义命令 1.宏定义命令的格式 宏定义命令分为不带参数的宏定义命令和带参数的宏定义命令两种。 不带参数的宏定义命令格式如下: #define <标识符> <串> 带参数的宏定义命令格式如下: #define <宏名>(<参数表>)<宏体> 见书中例3.3。 2.使用宏定义命令时应注意的事项 ① 宏定义命令所定义的宏名的作用域是文件级的。取消宏定义命令undef <标识符>可以用来取消被定义的宏名。 ② 宏定义命令可以嵌套。 ③ 带参数的宏定义中,对于宏体中出现的参数应适当地加以括号很重要,这样可以避免因优先级引起的误解。 见书中例3.4。 3.2 表达式语句和复合语句 3.2.1 表达式语句和空语句 1.表达式语句 使用表达式在其后加一个分号(;),便组成了表达式语句。 表达式语句和表达式是有区别的。 2.空语句 空语句是只有一个分号(;),而无任何表达式的语句。空语句是一条最为简单的特殊语句。 3.2.2 复合语句和分程序 1.复合语句 复合语句是由两条或两条以上的语句用花括号({})括起来组成的。 2.分程序 分程序是一种含有说明语句的复合语句。 语句按其功能可分为说明语句和执行语句两大类。 函数体和分程序是不同的。 3.3 选择语句 3.3.1 条件语句 1.条件语句的格式 条件语句的格式如下: 22 if(<条件1>) <语句1> else if(<条件2>) <语句2> else if(<条件3>) <语句3> … . else if(<条件n>) <语句n> else <语句n+1> 2.条件语句的功能 条件语句的功能描述如下。 先计算<条件1>给定的表达式的值。如果该值为非0时,则执行<语句1>,执行后转到该条件语句后面的语句;如果该值为0,则计算<条件2>给定的表达式的值。同样地,该值为非0,则执行<语句2>,执行后退出该条件语句。按上述方法处理。如果所有<条件>给出的表达式值都为0,则执行<条件n+1>,执行后退出if语句。 两路分支的条件语句格式如下: if(<条件>) <语句1> else <语句2> 最简单的条件语句格式如下: if(<条件>) <语句> 3.条件语句举例 见书中例3.6,倒3.7。 3.3.2 开关语句 1.开关语句的格式 开关语句格式如下: switch(<整型表达式>) { case <整常型表达式1>:<语句序列1> case <整常型表达式2>:<语句序列2> … case <整常型表达式n>:<语句序列n> default: <语句序列n+1> } 2.开关语句功能 开关语句功能描述如下: 23 先计算switch后面括号内的表达式的值,再将该值与花括号内case子句中的<整常型表达式>的值进行比较。先与<整常型表达式1>值比较,如果不相等,再与后边的<整常型表达式2>的值比较,如果还不相等,则依次进行比较,直到<整常型表达式n>都不相等,执行default后面的<语句序列n+1>,执行完毕后,退出该开关语句。在用<整型表达式>与<整常型表达式>比较中,一旦有相等时,则执行该<整常型表达式>后面对应的<语句序列>。在执行<语句序列>的各个语句时,遇到break语句时,则退出该开关语句。如果遇不到break语句时,则依次执行其后的<语句序列>,直到开关语句的右花括号,再退出该开关语句。 使用开关语句时,应注意如下事项。 ① 开关语句中case子句的表达式是整常型表达式。 ② 通常的<语句序列>中最后一条语句是break,表示退出该开关语句。 ③ 在开关语句中,default子句可以被省略,它也可以出现在花括号内的任意位置。 ④ 开关语句可以嵌套。 ⑤ 在开关语句的<语句序列>中,使用break语句是很重要的。 3.开关语句举例 见书中例3.8,例3.9,例3.10。 3.4 循环语句 3.4.1 while循环语句 1.while循环语句格式 while循环语句格式如下: while(<条件>) <语句> 2.while循环语句功能 该循环语句功能如下: 先计算<条件>给定的表达式的值,如果其值为非0时,执行循环体<语句>,再计算<条件>给定的表达式的值,如果其值还是非0时,再执行一次循环体<语句>,直到<条件>表达式的值为0时,退出该循环语句,执行其后面的语句。 使用while循环语句时应注意下述事项。 ① 执行while循环语句时,先计算<条件>给出的表达式的值。如果第一次计算的表达式值为0时,一次循环体也不执行。 ② 如果循环语句中给定的表达式值永远为非0时,而循环体内又无退出循环的语句,则为无限循环。 ③ 该循环语句可以嵌套。 3.while循环语句举例 见书中例3.11。 3.4.2 do-while循环语句 1.do-while循环语句格式 该循环语句格式如下: do <语句> 24 while(<条件>); 2.do-while循环语句的功能 该循环语句功能如下: 先执行一次循环体<语句>,再计算<条件>中给定的表达式的值。如果该表达式的值为非0时,则再执行循环体,直到其值为0时,退出循环语句。 使用do-while循环语句时应注意如下事项。 ① 该循环语句的特点是无论<条件>如何,至少执行一次循环体<语句>。 ② 该循环语句可以用while循环语句表示,其格式如下: <语句> while(<条件>) <语句> ③ 该循环语句可以嵌套。 3.do-while循环语句举例 见书中例3.12。 3.4.3 for循环语句 1.for循环语句的格式 for循环语句格式如下: for(d1; d2; d3) <语句> 2.for循环语句的功能 for循环语句功能如下: 先计算表达式d1的值,再计算表达式d2的值,判断是否执行循环体。如果表达式d2值为0时,则退出该循环语句,执行该循环语句后面的语句;如果表达式d2的值为非0时,则执行循环体<语句>,再计算表达式d3,改变循环变量的值。接着,再计算表达式d2,然后判断是否执行循环体,重复前面操作。总之,每次计算的表达式d2的值不为0时,便执行循环体,只有d2的值为0时,才会退出该循环语句。 使用for循环语句应注意下述事项。 ① 该循环语句通常用于循环次数事先能够确定的情况。 ② 该循环语句可以用while循环语句表示如下: d1; while(d2) { <语句> d3; } ③ 该循环语句使用灵活,形式多样。 3.for循环语句应用举例 见书中3.13,例3.14。 3.4.4 多重循环 多重循环又称为循环嵌套,多重循环是指在某个循环语句的循环体内还可以包含有 25 循环语句。 见书中例3.15,例3.16,例3.17。 3.5 转向语句 3.5.1 goto语句 该语句形式如下: goto <语句标号>; C++程序中限制goto语句的使用范围,规定该语句只能在一个函数体内转向,不允许从一个函数体转向到另一个函数体。 见书中例3.18。 3.5.2 break语句 该语句格式如下: break; break语句在C++程序仅可用于下述两种情况。 ① 用于开关语句的<语句序列>中,其功能是退出该开关语句。 ② 用于循环语句的循环体中,其功能是退出该重循环。 见书中例3.19。 3.5.3 continue语句 该语句格式如下: continue; 该语句只用于循环语句的循环体中,其功能是用来结束本次循环。 见本书例3.20。 作业题和上机练习题 26 第4章 函数和作用域 4.1 函数的定义和说明 4.1.1 函数的定义 1.函数的定义格式 C++语言中,函数的定义格式如下: <类型> <函数名> (<形参表>) { <函数体> } 其中,<类型>包含存储类和数据类型。存储类对函数来讲有两种:一种是外部函数,存储类说明符为extern,通常被缺省;另一种是内部函数,存储类说明符为static,该说明符不可省略。 下面列举几个简单函数的定义。 void nothing () { } 又例如, void fun1() { cont <<\"ok!\" <
; 函数返回值的具体实现过程描述如下。 ① 执行带有返回值的return语句时,先计算return关键字后边的<表达式>的值。 ② 根据函数的类型来确定表达式的类型。如果表达式类型与函数类型不一致时,强行将表达式类型转换为函数类型。 ③ 将表达式的值作为函数的返回值传递给调用函数,作为调用函数的值,通常调用函数将其值赋给某个同类型的变量,或者输出显示。 ④ 将程序的执行顺序转回到调用函数的语句,接着执行调用函数下面的语句。 函数的类型是定义函数时给出的类型,C++语言要术定义函数时都要指定类型。 4.2.2.函数的传值调用 1. 传值调用的实现机制和特点 这种调用方式的数据传递机制如下: 实参用表达式,形参用变量名,在函数调用时,用实参值对形参变量进行初始化。这种传递数据的特点是实参将拷贝一个副本给形参。 这种调用方式的特点如下: 由于传值调用的机制是实参拷贝副本给形参,于是在被调用函数中通过形参只能改变副本中实参传递过来的值,而无法改变实参变量的值。 见书中例4.3。 2. 传址调用的实现机制和特点 这种调用方式的数据传递机制如下: 这种调用要求实参用地址值,形参用同类型的指针。在函数调用时,将用实参的地址值初始化形参的指针,即使形参的指针指向实参的变量。 这种调用方式的特点如下: 由于传址调用的机制是用形参指针指向实参变量,因此很容易在被调用函数中通过指针来改变调用函数的实参值。 见书中例4.4。 4.2.3 函数的引用调用 28 引用调用方式数据传递的机制如下: 引用调用要求函数的实参用变量名或对象名,形参是引用名,用变量名对形参引用进行初始化。在引用调用中,不拷贝实参的副本。实际上只传递地址,使形参变成了实参的引用。 引用调用具有如下特点: 引用调用可以在被调用函数中通过改变形参引用的值来改变调用函数中的实参值。 见书中例4.5,例4.6。 4.3 函数的参数 4.3.1 函数参数的求值顺序 当一个函数具有多个实参时,允许不同编译系统在计算函数实参时有不同的计算顺序。在多个参数中出现了具有副作用的运算符时,不同的求值顺序可能造成不同的计算结果。于是便可能出现二义性。为了避免这种二义性,应该避免函数实参中出现的带副作用的运算符。 见书中例4.7。 4.3.2 设置函数参数的默认值 C++语言允许设置函数参数的默认值。 关于设置函数参数默认值的规则如下。 ① 一个函数有多个参数时,可以给该函数的部分参数或全部参数设置默认值。 ② 在给函数的部分参数设置默认值时,应该从参数表的右端开始,在设置了默认值的参数的右端不允许出现没有设置默认值的参数。 ③ 如果一个函数需要说明时,默认的参数值应设置在函数的说明语句中,而不是函数的定义中。 ④ 在函数调用时,对应参数如果有实参值,则将用该实参值取代设置的默认值;如果没有给定实参值时,则用参数的默认值。 ⑤ 在给函数参数设置默认值时,可以用相同类型的常量、变量以及同类型表达式,也可以是函数,通常应使用全局量。 见书中例4.8,例4.9。 4.3.3 使用数组作函数参数 1. 形参和实参都用数组 见书中例4.10。 2. 形参用指针和实参用地址值 见书中例4.11。 3. 形参用引用和实参用数组名 见书中例4.12。 4.4 内联函数 1.内联函数的概念 29 在程序编译时,系统将程序中出现的内联函数调用表达式用该内联函数的函数体进行替换。这样处理虽然会增加目标代码,但是避免了因函数调用而产生的额外开销。 2.内联函数的定义方法 定义内联函数的方法很简单,即在函数头前面加关键字inline,其他与一般函数相同。 3. 使用内联函数应注意如下事项。 ① 内联函数的函数体内不允许出现循环语句和开关语句等大语句。如果内联函数的函数体内含有这些语句时,系统将它按普通函数处理。 ② 内联函数的函数体不宜过大,通常以1~5行为宜。过大会增加源程序的代码量。 ③ 在类结构中,在类体内定义的成员函数都是内联函数。 见书中例5.13。 4.5 重载函数 1.重载函数的概念 在C++语言中,引进了重载函数,允许同一个函数名对应着不同的实现。以求绝对值为例, 3个求绝对值的函数起一个名字abs。 int abs(int); long abs(long); double abs (double); 2.重载函数的选择规则 编译选择原则如下。 ① 重载函数至少要在函数的参数类型、参数个数和参数顺序上有所不同。根据重载函数的参数类型、参数个数和参数顺序的不同进行选择。 ② 重载函数选择是按下述先后顺序查找的,将实参类型与所有被调用的重载函数的形参类型一一比较。 先查找的是严格匹配的; 再查找通过类型转换可以匹配的; 最后是通过用户的强制类型转换达到匹配的。 3. 使用重载函数时应注意以下事项。 ① 不允许使用typedef语句定义的类型名来区分重载函数的参数。 ② 定义重载函数时,要注意同名函数应具有相同的功能。 ③ 重载函数中的形参如果设置了默认值,则会影响重载函数的选择。 见书中例4.14,例4.15。 4.6 函数的嵌套调用和递归调用 4.6.1 函数的嵌套调用 函数的嵌套调用是指当一个函数调用另一个函数时,被调用函数又再调用其他函数。 见书中例4.16。 4.6.2 函数的递归调用 30 递归调用的特点及过程。 见书中例4.17。 4.7 作用域 4.7.1 标识符的作用域规则 标识符只能在说明它或定文它的范围内是可见的,而在该范围之外是不可见的。 4.7.2 作用域的种类 程序级,文件级,类级,函数级和块级。 4.7.3 关于重新定义标识符的作用域规则 在某个作用域范围内定义的标识符在该范围内的子范围内可以重新定义该标识符。这时原定义的标识符在子范围内是不可见的,但却存在,出了子范围后,它又是可见的。 见书中例4.19。 4.7.4 局部变量和全局变量 1. 局部变量 局部变量的作用域是函数级或块级的,包括自动类变量、寄存器类变量和内部静态类变量。 2. 全局变量 全局变量的作用城是文件级或程序级的,包括外部类变量和外部静态类变量。 见例4.21和例4.22。 4.7.5 内部函数和外部函数 1. 内部函数 内部函数的作用域是定义在该函数的文件内。在程序的一个文件中定义的内部函数只能在该文件中调用,在该程序的其他文件中是不能调用的。 内部函数的定义格式如下: static <数据类型> <函数名>(<参数表>) { <函数体> } 见书中例4.23。 2. 外部函数 外部函数的作用域是整个程序,包含该程序的所有文件。 外部函数的定义格式如下: [extern] <数据类型> <函数名> (<参数表>) 31 { <函数体> } 见书中例4.24。 4.8 C++语言的系统函数 4.8.1 C++语言系统函数概述 见书中例4.25。 4.8.2 字符串处理函数 系统提供了一些字符串处理函数,存放在string.h文件中。 1. 字符串长度函数strlen( ) 该函数的功能是用来求出一个字符串的长度。字符串长度是指字符串中有效字符的个数。 该函数格式如下: int strlen (char *s) 见书中例4.26。 2. 字符串比较函数strcmp() 该函数的功能是对两个字符串进行比较,并返回其比较结果,即一个int型数。 函数格式如下: int strcmp (char *s1, char *s2) int strncmp(char *s1, char *s2, int n) 见书中例4.29。 3. 检索字符串函数index() 该函数的功能是用来检索在一个指定的字符串中第一次出某个指定字符的位置。 函数格式如下: char *index(char *p, char c) char *rindex(char *p, char c) 4. 字符串连接函数strcat() 该函数的功能是将两个给定的字符串连接成一个字符串。 该函数格式如下: char * strcat (char s1[ ], char s2[ ]) char * strncat (char s1[ ], char s2[ ], int n) 见书中例4.28。 5. 字符串复制函数strcpy() 该函数的功能是将一个指定的字符串复制到指定的字符数组或字符指针中。该函数返回指向复制后的字符串的指针。 函数格式如下: char *strcpy (char s1[ ], char s2[ ]) char *strncpy (char s1[ ],char s2[ ], int n) 见书中例4.27。 32 作业题和上机练习题 33 第5章 类和对象(一) 5.1 类的定义 5.1.1 类的定义格式 定义类的一般格式: //说明部分 class <类名> { public: <成员函数和数据成员的说明或实现> private: <数据成员和成员函数的说明或实现> }; //实现部分 <函数类型> <类名>:: <成员函数名>(<参数表>) { <函数体> } 5.1.2 定义类的注意事项 关于类定义的注意事项如下。 ① 定义类的关键字通常用class,也可以用struct等。 ② 类的定义由两大部分构成:说明部分和实现部分。 ③ 类的成员分为数据成员和成员函数两种。 ④ 类的成员具有访问权。类成员的访问权限有如下3种。 public(公有的):公有成员不仅在类体内是可见的,而且在类体外也是可见的。 private(私有的):私有成员仅在类体内是可见的,在类体外是被隐藏的。 protected(保护的):保护成员对于定义它的类来讲,相当于私有成员;对于该类的派生类来讲,相当于公有成员。 ⑤ 成员函数可以定义在类体内,也可以定义在类体外。 ⑥ 类体内不允许对数据成员初始化。 5.2 对象的定义 5.2.1 对象的定义格式 (1)先定义类类型,再定义对象 这种分开定义对象的格式如下: <类名> <对象名表>; (2)定义类类型同时定义对象 这种同时定义对象的格式如下: class <类名> 34 { <类体成员> }<对象名表>; (3)使用无名类直接定义对象 用无名类直接定义对象的格式如下: class { <类体成员说明与定义> }<对象名表>; 5.2.2 对象成员的表示方法 (1)一般对象的成员表示用运算符。 <对象名>.<数据成员名> <对象名>.<成员函数名>(<参数表>) (2)指向对象指针的成员表示用运算符-> <对象指针名> -> <数据成员名> <对象指针名> -> <成员函数名>(<参数表>) (3)对象引用的成员表示用运算符. <对象引用名>.<数据成员名> <对象引用名>. <成员函数名>(<参数表>) (4)对象数组元素的成员表示同一般对象 <数组名>[<下标>]. <成员名> 见书中例5.1,例5.2。 5.3 构造函数和析构函数 1.构造函数的功能 构造函数的主要功能就是用来初始化对象。 2.构造函数的种类 构造函数通常有如下3种。 (1)默认构造函数 这种构造函数的特点是不带参数。 默认构造函数用户可以定义。如果在一个类体中,用户没有定义任何构造函数时,系统会自动创建一个默认的构造函数。 (2)带参数的构造函数 构造函数可以带有一个或者多个参数。 (3)拷贝构造函数 拷贝构造函数是用来使用已知对象给所创建对象进行初始化时所用的构造函数。 拷贝构造函数的格式如下: <构造函数名> (<类名> & <对象引用名>) { <函数体> } 35 如果一个类中,用户没有定义拷贝构造函数时,系统自动创建一个默认的拷贝构造函数。 3.构造函数的特点 构造函数还具有与一般成员函数不同的特点。 ① 构造函数的名字同类名。 ② 说明或定义构造函数时不必指出类型,也无任何返回值。 ③ 构造函数是系统在创建对象时自动调用的。 4. 析构函数的功能和特点 析构函数的功能正好与构造函数相反,它是用来释放所创建的对象的。一个对象在它的寿命结束时,系统将会自动调用析构函数将它释放掉 析构函数与一般成员函数不同的特点。 ① 析构函数名同类名,为与构造函数区别在析构函数名前加“~”符号。 ② 析构函数定义时不必给出类型,也无返回值,并且无参数。 ③ 析构函数是由系统自动调用。 如果一个类体内,用户没有定义析构函数时,系统会自动创建一个默认的析构函数。 析构函数由于没有参数,它不能被重载。 见书中例5.3,例5.4,例5.5。 5.4 成员函数的特性 5.4.1 内联函数和外联函数 C++语言规定,成员函数如果被定义在类体内,则为内联函数。如果要使定义在类体外的函数也为内联函数,需在定义时在函数头前加上关键字inline。 见书中例5.6。 5.4.2 重载性 成员函数可以重载,重载时应遵循参数可以区别的规则。 见书中例5.7。 5.4.3 设置参数的默认值 成员函数的参数可以设置为默认值。 见书中例5.8。 5.5 静态成员 在类体内使用关键字static说明的成员称为静态成员。静态成员包括静态数据成员和静态成员函数两种。 5.5.1 静态数据成员 静态成员的特点是它不是属于某对象的,而是属于整个类的,即所有对象的。 36 1.静态数据成员的说明方法和初始化 对静态数据成员初始化的方法是在类体外使用如下格式进行初始化: <数据类型> <类名>::<数据成员名>=<初值>; 由于静态数据成员不是属于某个对象的,可以通过类名加作用域运算符进行引用。具体格式如下: <类名>::<静态数据成员名>; 2.静态数据成员的特点 ① 静态数据成员不是属于某个对象,而是属于整个类的。 ② 静态数据成员不随对象的创建而分配内存空间,它也不随对象被释放而撤销。只有在程序结束时才被系统释放。 ③ 静态数据成员只能在类体外被初始化 见书中例5.10。 5.5.2 静态成员函数 说明静态成员函数格式如下: static <类型> <成员函数名>(<参数表>); 引用静态成员函数有如下两种方式: <类名>::<静态成员函数名>(<参数表>) 或者 <对象名>. <静态成员函数名>(<参数表>) 在静态成员函数中可以直接引用其静态成员,而引用非静态成员时需用对象名引用。 见书中例5.11。 5.6 友元 5.6.1 友元函数 友元函数是说明在类体内的一般函数,它不是这个类中的成员函数,但是它访问该类所有成员。 友元函数说明格式如下: friend <类型> <函数名> (<参数表>) 使用友元函数时应注意如下几点。 ① 友元函数前边加friend关键字,说明在类体内。如被定义在类体外,不加类名限定。 ② 友元函数可以访问类中的私有成员和其他成员。 ③ 友元函数的作用在于可以提高程序的运行效率。。 ④ 友元函数在调用上同一般函数。 见书中例5.12。 5.6.2 友元类 将一个类作为另一个类的友元,则该类称为友元类。友元类中的所有成员函数都是这个类的友元函数。 37 说明友元类的形式如下: friend class <类名>; 使用友元类应注意下述事项。 ① 友元关系是不可逆的。B类是A类的友元类,不等于A类是B类的友元类。 ② 友元关系是不可传递的。B类是A类的友元类,C类是B类的友元类,C类不一定就是A类的友元类。 见书中例6.14。 5.7类的作用域 类的作用域是指类体的花括号所括的范围,一个类的成员在类体内是可见的。由于成员的访问权限不同,在类体外的可见性要具体分析。 5.8 局部类和嵌套类 5.8.1 局部类 在一个函数体内定义的类称为局部类。 见书中p188的例子。 5.8.2 嵌套类 在一个类中定又的类称为嵌套类。 见书中189的例子。 5.9 对象的生存期 在C++语言中,对象的存储类有如下3种: ① 局部对象; ② 全部对象; ③ 静态对象(又分内部静态与外部静态)。 1.局部对象 局部对象是被定义在一个函数体内或一个分程序中,其作用域是该函数体或该分程序内。 2.全局对象 全局对象是被定义在某个文件中,它的作用域是整个程序。 3.静态对象 静态对象按其作用域不同又分为内部静态对象和外部静态对象两种。内部静态对象的作用域是定义它的函数体或分程序内;外部静态对象的作用域是定义它的文件,并从定义时开始。 见书中例5.15。 作业题和上机练习题 38 第6章 类和对象(二) 6.1 对象指针和对象引用 6.1.1 指向类的成员的指针 1. 指向类的数据成员的指针 2. 指向类的成员函数的指针 见书中例6.1。 6.1.2 对象指针和对象引用作函数参数 1. 对象指针作函数参数 见书中例6.2。 2. 对象引用作函数参数 见书中例6.3。 6.1.3 对象引用 对象引用的定义格式如下: <类名> & <对象引用名>=<对象名> 对象引用常用来作函数的形参。当函数形参为对象引用时,则要求实参为对象名,实现引用调用。 见书中例7.5。 6.2 对象数组和对象指针数组 6.2.1 对象数组 1. 对象数组的定义 相同类的若干个对象的集合构成一个对象数组。对象数组的定义格式如下。 <类名> <对象数组名> [<大小>]… 2. 对象数组的赋值 对象数组元素可以被赋值。 见书中例6.5。 6.2.2 指向对象数组的指针和对象指针数组 1. 指向对象数组的指针 指向对象数组的指针可以指向一维对象数组,也可以指向二维对象数组。 指向一维对象数组的一级指针定义格式如下: <类名> (*<指针名>)[<大小>] 见书中例6.7。 2. 对象指针数组 对象指针数组是指数组的元素是指向对象的指针,并要求所有数组元素都是指向相同 39 类的对象的指针。其格式如下: <类名> *<对象指针数组名>[<大小>]… 对象指针数组可以被初始化,也可以被赋值。 见书中例6.8,例6.9。 6.2.3 带参数的main()函数 带参数的main()函数头格式如下: void main(int argc.char *argv[]) 见书中侧6.10。 6.3 常类型 6.3.1 一般常类型和对象常量 1. 一般常类型 一般常类型的格式如下: <类型说明符> const <变量名> 2. 常对象 常对象格式如下: <类名> const <对象名>(<初值>) 6.3.2 常指针和常引用 1. 常指针 (2) 地址值为常量的指针 格式如下: <类型> *const <指针名>=<初值> (3) 所指向的值为常量的指针 格式如下: const <类型> *<指针名>=<常量> 2. 常引用 格式如下: const <类型> &<引用名>=<初值> 见书中例6.11,例6.12。 6.3.3 常成员函数 格式如下: <类型> <函数名>(<参数表>) const 见书中例6.13。 6.3.4 常数据成员 40 常数据成员使用修饰符是const,加上数据成员的左边。 常数据成员的初始化使用的是构造函数中的成员初始化列表。 见书中侧6.14。 6.4 子对象和堆对象 6.4.1 子对象 在一个类中可以使用另一个类的对象作其数据成员,这种对象的数据成员称为子对象。子对象反映两个类之间的包含关系。 子对象初始化应放在构造函数的成员初始化列表中。成员初始化列表放在构造函数的函数头的后面,用冒号进行分隔。具体格式如下: <构造函数名>(<参数表>): <成员初始化列表> { <函数体> } 见书中例6.15。 6.4.2 堆对象 1.使用new运算符创建堆对象 ① 使用new运算符创建一个对象或其他类型变量的格式如下: new <类名>; 或者 <类型说明符> (<初值>); ② 使用new运算符创建一个对象数组或其他类型数组的格式如下: new <类名>; 或者 <类型说明符>[<大小>]; 对象数组创建后可使用如下语句,判断创建是否成功: if(parray= =NULL) { cout<<\"数组创建失败!/n\"; exit(1); } 使用new所创建的数组,可以给其元素赋值。 2.使用delete运算符释放对象 delete运算符的功能是用来释放使用new运算符创建的堆对象和堆对象数组的。 ① 使用delete运算符释放对象或变量的格式如下: delete <指针名>; ② 使用delete运算符释放对象数组或其他类型数组的格式如下: delete [ ]<指针名>; 见书中例6.16,例6.17。 41 6.5 类型转换 6.5.1 类型的自动隐含转换 C++语言编译系统提供内部数据类型的自动隐含转换规则如下。 ① 在执行算术运算时,低类型自动转换为高类型。 ② 在赋值表达式中,赋值运算符右边表达式的类型自动转换为左边变量的类型。 ③ 在函数调用时,将调用函数的实参初始化形参,系统将实参转换为形参类型后,再进行传值。这里的隐含转换通常是低类型转换为高类型。 ④ 在函数有返回值时,系统自动将返回的表达式的类型转换为该函数的类型后,再将表达式的值返回给调用函数。 在程序中,出现上述转换时,如果数据精度受损失,系统会报错。 6.5.2 一般数据类型转换为类类型 使用一个参数的构造函数可将某种数据类型转换为该构造函数所属类的类型。 见书中例6.19。 6.5.3 类类型转换为一般数据类型 通过在类中定义类型转换函数可以实现由某种类类型转换为某种指定的数据类型的操作。 类型转换函数定义格式如下: operator <数据类型说明符>() { <函数体> } 见书中例6.20。 作业题和上机练习题 42 第7章 继承性和派生类 7.1 基类和派生类 基类和派生类反映了类与类的继承关系,派生类继承了基类,派生类的成员中包含了基类的所有成员,并且派生类还有自己的成员。 派生类是用来生成新类的一种方法,所生成的新类与原类有一种所属的关系。 基类和派生类是相对而言的。一个基类可以派生出一个或多个派生类,而每个派生类又可作基类再派生出新的派生类,如此一代一代地派生下去,便形成了类的继承层次结构。 7.1.1 派生类的定义格式 派生类可以是单重继承的派生类,也可以是多重继承的派生类,两者的区别仅在于所继承基类数不同。这里,仅以单重继承的派生类为例。 派生类定义格式如下: class <派生类名>:<继承方式> <基类名> { <派生类新增成员说明> }; 7.1.2 派生类的三种继承方式 继承方式包含以下3种: ① public(公有的方式); ② private(私有的方式); ③ protected(保护的方式)。 默认方式:对class来讲是private;对struct来讲是public。 7.1.3 基类成员在派生类中的访问权限 基类成员在不同的继承方式下,在派生类的访问权限规则如下。 ① 基类中的私有成员无论哪种继承方式在派生类中都是不能直接访问的。 ② 在公有继承方式下,基类中公有成员和保护成员在派生类中仍然是公有成员和保护成员。 ③ 在私有继承方式下,基类中公有成员和保护成员在派生类中都为私有成员。 ④ 在保护继承方式下,基类中公有成员和保护成员在派生类中都为保护成员。 将这4条规则用表8.1表示如下。 表8.1 基类中成员 私成员 公成员 保护成员 基类成员在派生类中访问权限 公有继承方式 不可访问 公有 保护 私有继承方式 不可访问 私有 私有 保护继承方式 不可访问 保护 保护 为了记忆方便,读者可记住以下4句话: 43 基类私有不可访问,公有不变,私有私有,保护保护。 7.1.4.成员访问权限的控制 (1)公有继承方式 在公有继承方式下,派生类可以访问从基类中继承的基类的公有成员和保护成员,还可以访问派生类中定义的所有成员。派生类的对象只能访问基类中的公有成员和派生类中定义的公有成员。 见书中例7.1。 (2)私有继承方式 在私有继承方式下,派生类可访问从基类中继承的公有成员和保护成员,它们在派生类中是私有的,还可以访问派生类自身的所有成员。派生类对象仅能访问派生类自己的公有成员。 见书中例7.2。 (3)保护继承方式 在保护继承方式下,派生类可访问基类的公有成员和保护成员,也可访问派生类自身的所有成员。派生类的对象只能访问派生类自身的公有成员。保护继承方式可以使基类中的公有成员不被派生类的对象访问,又可以基类的保护成员被派生类访问。 见书中例7.3。 7.2 单继承 7.2.1 派生类的构造函数和析构函数 1.派生类的构造函数 派生类的构造函数应该包含它的直接基类的构造函数。定义格式如下: <派生类构造函数名> (<总参数表>):<基类构造函数名> (<参数表>),<其他初始化项> { <派生类自身数据成员初始化> } 掌握派生类构造函数时应注意如下两点。 ① 派生类构造函数的执行顺序如下: 先执行基类构造函数; 再执行子对象的构造函数(如有子对象的话); 最后执行派生类构造函数的函数体。 ③ 派生类构造函数的成员初始化列表中应该显式地包含基类中带参数的构造函数,或者隐含地包含基类中的默认构造函数。 见书中例7.4。 2.派生类的析构函数 由于析构函数不能继承,因此在派生类的析构函数中要包含它的直接基类的析构函数。 派生类析构函数的执行顺序如下: 先执行派生类析构函数的函数体; 再执行子对象所在类的析构函数(如果有子对象的话); 最后执行直接基类中的析构函数。 44 见书中例7.5,例7.6。 7.2.2 子类型和赋值兼容规则 1.子类型 当一个类型至少包含了另一个类型的所有行为,则称该类型是另一个类型的子类型。例如,在公有继承下,派生类是基类的子类型。 如果类型A是类型B的子类型,则称类型A适应于类型B,这时用类型B对象的操作也可以用于类型A的对象。因此,可以说类型A的对象就是类型B的对象。 子类型的关系是不可逆的。 关于类型之间的关系,到目前为止有如下3种。 ① 类型相同。 ② 类型匹配。。 ② 类型适应。 2.赋值兼容规则 当类型A是类型B的子类型时,则满足下述的赋值兼容规则。 ① A类的对象可以赋值给B类的对象。 ② A类的对象可以给B类对象引用赋值。 ③ A类的对象地址值可以给B类对象指针赋值。 见书中例7.7。 7.3 多继承 7.3.1 多继承的概念 多继承是指派生类具有多个基类的继承。 多继承派生类的定义格式如下: class <派生类> :<继承方式1> <基类名1> ,<继承方式2> <基类名2>… { <派生类类体> } 7.3.2 多继承派生类的构造函数和析构函数 多继承派生类构造函数格式如下: <派生类构造函数名> (<总参数表>): <基类名1> (<参数表1>), <基类名2> (<参数表2>),… { <派生类构造函数体> } 在多继承派生类构造函数中,先执行基类的构造函数。多个基类构造函数的执行顺序取决于定义派生类时规定的先后顺序,与派生类的成员初始化列表中顺序无关。 同样地,派生类构造函数中可以隐含着直接基类的默认构造函数。 多继承派生类的析构函数中也隐含着直接基类的析构函数,但其执行顺序与构造函数相 45 反。 见书中例7.8。 7.3.3 多继承的二义性 在多继承时,在下面两种情况情况下,可能出现派生类对基类成员访问的二义性。 1. 调用不同基类中的相同成员时可能出现二义性 2.访问共同基类的成员时可能出现二义性 当一个派生类中有多个基类,而这些基类又有一个共同的基类时,访问这个公共基类中的成员时,可能出现二义性。 见书中例7.9。 7.4 虚基类 7.4.1 虚基类的引入和说明 虚基类说明格式如下: virtual <继承方式> <基类名> 7.4.2 含有虚基类的派生类的构造函数 在派生类的构造函数的初始化列表中除了包含其直接基类的构造函数外,还包含虚基类的构造函数。C++语言规定,虚基类的构造函数的执行优先于非虚基类构造函数。 见书中例7.10。 作业题和上机练习题 46 第8章 多态性和虚函数 8.1 函数重载 见书中例8.1。 8.2 运算符重载 8.2.1 运算符重载的几个问题 1.哪些运算符可以重载 大多数运算符都可以重载,只有少数运算符不能重载。 2.运算符重载遵循的“4个不变”原则 ① 操作数个数不变; ② 优先级不变; ③ 结合性不变; ④ 语义不变。 3.重载运算符在程序中的选择 重载运算符的选择主要是根据运算符的操作数的个数、类型及顺序的不同来选择的。 4.使用运算符重载应注意的问题 ① 运算符重载是通过函数定义来实现的,在定义运算符重载的函数时不能设置函数的默认值。 ② 重载运算符的定义方法通常采用成员函数方法或友元函数方法。 ③ 通常不随意定义重载运算符,而尽量使得重载运算符的功能与其原来用于标准数据类型的功能相似。 ④ 用于类对象的运算符一般都要重载。 8.2.2 运算符重载的两种方法 1.成员函数方法 运算符重载采用成员函数方法的形式如下: <类型> operator <运算符>(<参数表>) { <函数体> } 见书中例8.2。 2.友元函数方法 重载运算符采用友元函数的形式格式如下: friend <类型> operator <运算符> (<参数表>) { 47 <函数体> } 见书中例8.3,例8.4。 8.2.3 运算符重载举例 1.下标运算符重载 见书中例8.5。 2. 增1减1运算符重载 见书中例8.6。 3. 函数调用运算符重载 见书中例8.7。 8.3 静态联编和动态联编 8.3.1 静态联编 “联编”的含义是指对于相同名字的若干个函数的选择问题题。 在C++语言中,可分为静态联编和动态联编两种。静态联编是在编译时进行的,又称早期联编。 见书中例8.8。 8.3.2 动态联编 动态联编是在运行阶段进行的,又称滞后联编。 动态联编需要满足一定条件。公有继承是基础,虚函数是关键,对虚函数方式也很重要。 8.4 虚函数 8.4.1 虚函数的概念 1.虚函数的说明方法 虚函数是非静态成员函数。 说明虚函数的格式如下: virtual <类型> <成员函数名> (<参数表>) 2.虚函数的作用 虚函数是实现动态联编的关键。 虚函数可以通过基类指针或引用访问基类和派生类中被说明为虚函数的同名函数。 3.虚函数的特征 虚函数具有继承性。 8.4.2 动态联编举例 48 1.使用对象指针或对象引用调用虚函数时,可以实现动态联编 见书中例8.9。 2.使用成员函数调用虚函数可以实现动态联编 见书中例8.10,例8.11。 3. 使用构造函数调用虚函数实现静动态联编 见书中侧8.12。 8.5 纯虚函数和抽象类 8.5.1 纯虚函数 纯虚函数是一种特殊的虚函数,它是一种没有具体实现的虚函数。 纯虚函数格式如下: virtual <类型> <函数名> (<参数表>) = 0; 见书中例8.15。 8.5.2 抽象类 1.抽象类的概念 凡是包含有一个或多个纯虚函数的类称为抽象类。 抽象类不能定义对象,,但是它可以定义对象指针或对象引用。 抽象类不同于普通的具体类,它是更高级的抽象。 2.抽象类的作用 抽象类在类的层次结构中,作为顶层或最上面几层的,由它作为一个类族的公共接口。 抽象类是一种公共接口,它是下面诸多的派生类的集中归宿。 见书中例8.18。 8.6 虚析构函数 析构函数可以说明为虚函数,其方法也是在析构函数头前边加关键字virtual。 虚析构函数的作用在于系统将采用动态联编调用虚析构函数。 见书中例8.17。 作业题和上机练习题 49 第9章 C++语言的I/O流库 数据流按其流向可分为输入流和输出流两种。输入流指的是字节流从输入设备流向内存。输出流指的是字节流从内存流向输出设备,。 输入流和输出流都是带有内存缓冲区的。 在C++语言中,将输入流和输出流都分别定义为类,这些类放在C++语言的I/O流类库中,使用它们定义的对象称为流对象。 9.1 屏幕输出操作 9.1.1 使用预定义的插入符 这是一种最简单的,也是最常用的方式。 格式如下: cout << <表达式> 见书中例9.1,例9.2,例9.3。 9.1.2 使用成员函数put()输出一个字符 格式如下: ostream & <流对象名>.put (char c); 见书中例9.4, 9.1.3 使用成员函数write()输出一个字符串 格式如下: cout.write (const char *str, int n) 见书中例9.5,例9.6。 9.2 键盘输入操作 9.2.1 使用预定义的提取符 格式如下: cin >> <变量名> 从键盘上输入数据是带缓冲区的,输入完数据按回车键时才形成输入流。 输入流中数据项的默认分隔符为空白符。 见书中例9.7,例9.8。 9.1.2 使用成员函数get()获取一个字符 格式如下: char istream::get() 50 使用成员函数getline()读取一行字符 格式如下: cin.getline(char *buf,int n,char deline='\\n') 见书中例9.9,例9.10。 9.1.3 使用成员函数read()读取若干字符 格式如下: cin.read (char *buf,int n) 见书中例9.11,例9.12。 9.3 输入符和提取符的重载 可以通过对输入符和提取符的重载来支持新的数据类型。 见书中例9.13,例9.14。 9.4 格式输出操作 9.4.1 使用流对象的成员函数进行格式输出 1.控制输入/输出格式的标志位 用来控制输入/输出格式的标志位如表10.1所示。 表10.1 标 志 位 skipws left right internal dec oct hex showbase showpoint uppercase showpos scientific fixed unitbuf stdio 值 0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080 0x0100 0x0200 0x0400 0x0800 0x1000 0x2000 0x4000 ios标志位 含 义 跳过输入中的空白符 输出数据按输出域左对齐 输出数据按输出域右对齐 数据的符号左对齐,数据本身右对齐,符号和数据之间为填充符 转换基数为十进制形式 转换基数为八进制形式 转换基数为十六进制形式 输出的数值数据前面带有基数符号(0或0x) 浮点数输出带有小数点 用大写字母输出十六进制数值 正数前面带有“+”符号 浮点数输出采用科学表示法 使用定点数形式表示浮点数 完成输入操作后立即刷新流的缓冲区 完成输入操作后刷新系统的stdout.stderr 输入/输出 I O O O O I/O I/O I/O O O O O O O O 2.控制输出格式的成员函数 下面介绍一些用来控制输出格式的成员函数。 (1)设置标志字的成员函数 long flags() 该函数返回当前标志字。 long flag(long) 51 该函数使用参数更新标志字,并返回更新前的标志字。 long setf(long setbits, long field) 该函数用来将field参数所指定的标志位清零,将setbits为1的标志位置1,并返回设置前的标志字。 long setf(long) 该函数用来设置参数的指定的那些标志位,并返回更新前的标志字。 long unsetf(long) 该函数用来清除参数所指定的那些标志位,并返回更新前的标志字。 (2)设置输出数据所占宽度的成员函数 int width() 该函数用来返回当前输出的数据宽度。 int width(int) 该函数用来用其参数设置当前输出的数据宽度,并返回更新前的宽度值。 (3)设置填充符的成员函数 char fill() 该函数用来返回当前所用的填充符。 char fill(char) 该函数用来设置当前的填充符为参数给定的字符,并返回更新前的填充符。 (4)设置浮点数输出精度的成员函数 int precision() 该函数用来返回当前浮点数的有效数字的个数。浮点数的精度是用有效数字个数来表示的,其个数越大,表示精度越高。 int precision(int) 该函数用来设置当前浮点数输出时有效数字个数为该函数所指定的参数值,并返回更新前的值。 下面给出在默认情况下的某些参数的值: ① 数据输出宽度默认情况下为实际宽度; ② 默认情况下填空符为空格符; ③ 单精度浮点数最多提供7位有效数字,双精度浮点数最多提供15位有效数字,长双精度浮点数最多提供19位有效数字。 见书中例9.15,例9.16。 9.4.2 使用控制符进行格式输出 表10.2中给出了I/O流类库中定义的控制符。使用这些控制符时需包含iomarip.h头文件。 表10.2 操 作 子 名 dec hex oct setbase(int n) ws ends 流类库所定义的操作子 含 义 数值数据采用十进制表示 数值数据采用十六进制表示 数值数据采用八进制表示 设置数制转换基数为n(n为0,8,10,16) 0表示使用默认基数 提取空白符 插入空字符 输入/输出 I/O I/O I/O I/O I O 52 flush resetiosflags(long) setiosflags(long) setfill(int) setprecision(int) 刷新与流相关联的缓冲区 清除参数所指定的标志位 设置参数所指定的标志位 设置填充字符 设置浮点数输出的有效数字个数 设置输出数据项的域宽 O I/O I/O O O O setw(int) 见书中例9.17。 9.5 磁盘文件的操作 9.5.1 打开文件和关闭文件操作 1.打开文件操作 打开文件通常分为两步:先创建流对象,再使用成员函数open()打开指定的文件。 创建流对象又可分两种情况,一种情况是创建fstream类的对象,另一种情况是创建ifstream类或ofstream类的对象。 (1)通过创建fstream类对象打开文件的方法 方法一:先创建对象,再打开文件 格式如下: fstream <对象名>; <对象名>.open(\"<文件名>\",<访问方式>); 表10.3 方 式 名 in out app ate trunc binary nocreate noreplace ios::inlios::out ios::outlios::binary ios::inlios::binary 以输入(读)方式打开文件 以输出(写)方式打开文件 以输出追加方式打开 文件打开时,文件指针位于文件尾 如果文件存在,将其长度截断为0,并清除原有内容;如果文件不存在,则创建新文件 以二进制方式打开文件,默认时为文本方式 打开一个已有文件,如该文件不存在,则打开失败 如果文件存在,除非设置ios::ate或ios::app,否则打开操作失败 以读和写的方式打开文件 以二进制写方式打开文件 以二进制读方式打开文件 文件访问方式常量 用 途 方法二:创建对象和打开文件合二为一 其格式如下: fstream <对象名> (\"<文件名>\<访问方式>); 与前面方法相比较,省略了打开函数的名字open。 (2)通过创建istream类对象或ostream类对象打开文件的方法 方法一:先创建对象,再打开文件 格式如下: ofstream <对象名>; <对象名>.open (\"<文件名>\"); 或者 ifstream<对象名>; 53 <对象名>.open(\"<文件名>\"); 方法二:创建对象同时打开文件 格式如下: ofstream <对象名> (\"<文件名>\"); 或者 ifstream <对象名> (\"<文件名>\"); 2.关闭文件 格式如下: <对象名>.close(); 9.5.2 文本文件的读写操作 在文本文件的读写操作之前,要先打开文件,打开文件时应先创建流对象,打开文件时应指出文件名和访问方式,文件打开后才可以进行读写操作,操作完毕后还要关闭文件。 见书中例10.19,例9.20,例9.21,例9.22。 9.5.3 二进制文件的读写操作 二进制文件的读写操作与文本文件的读写操作基本相同,所不同的仅在于对二进制文件来说打开时需要加上ios::binary方式。另外,用于二进制文件读写函数通常使用read()函数和write()函数。 见书中例9.23。 9.5.4 随机文件操作 C++语言中所提供的定位读、写指针的函数。 ① 在类istream中定义的定位读指针的成员函数有如下几种: istream & istream::seekg (<流中位置>); istream & istream::seekg(<偏移量>, <参照位置>); long int & istream::tellg(); ② 在类ostream中定义的定位写指针的成员函数有如下几种: ostream & ostream::seekp(<流中位置>); ostream & ostream::seekp(<偏移量>, <参照位置>); long int ostream::tellp(); <相对位置>具有下述含义: cur=1 beg=0 end=2 // 相对于当前读/写指针所指的位置 // 相对于文件头的位置 // 相对于文件尾的位置 见书中例9.24,例9.25。 9.5.5 其他有关文件操作的函数 1. 跳过输入流中指定数量字符的函数 54 函数格式如下: istream & istream::ignore(int n=1,int t=EOF) 见书中例9.26。 2. 返回一个字符到输入流的函数 函数格式如下: istream & istream::putback(char ch) 见书中例9.27 9.6 字符串流 C++语言的I/O流库提供了两个类:ostrstream和istrstream用来进行字符串流的操作。 9.6.1 ostrstream类的构造函数 这里有两个构造函数,格式如下: ostrstream::ostrstream(); ostrstream::ostrstream(char *s,int n,int mode=ios::out); 见书中例9.28。 9.6.2 istrstream类的构造函数 这里有两个构造函数,格式如下: istrstream::istrstream(char *s); istrstream::istrstream(char *s,int n); 见书中例9.29。 作业题和上机练习题 55