3.23 小结
要开发和维护大程序,最好的办法是从更容易管理的小块和小组件开始。C++中的模块称为函数和类。
通过函数调用来调用函数。函数调用指定函数名和提供被调用函数完成任务所需的信息作为参数)。
信息隐藏的目的是函数只能访问完成任务所需要的信息,从而实现最低权限。这是良好的软件的最重要特性之一。
调用函数的格式为:先写上函数名,后面跟着左括号,然后是函数参数(或逗号分隔的参数表),最后是右括号。
double和float都是浮点数据类型。float类型变量能存放比double更大、精度更高的数值。
函数参数可取常量、变量或表达式。
局部变量只在声明该变量的函数中有效。函数不能知道任何其他函数的实现细节(包括局部变量)。
函数定义格式如下:
return--value—type function—name(parameter—list){ declarations and statements }
函数名是任何有效标识符,返回值类型是函数向调用者返回的值的数据类型.返回值类型为void表示函数没有返回值。参数表是逗号分隔的清单,包含传递给函数的参数的声明。如果函数不接受任何值,则参数表为void或空着。花括号中的声明和语句构成函数体。
函数定义和函数调用的形参和实参的个数、类型、顺序和返回值类型应该相符。
程序遇到函数时,控制从调用点转入被调用函数,执行该函数,然后控制返回调用者。
将控制返回函数调用点的方法有三种。如果函数不返回结果,则控制在到达函数结束的右花括号时或执行下列语句时返回:return;
如果函数返回结果,则下列语句:return expression;
向调用者返回表达式的值。
函数原型声明函数名称、函数返回的数据类型、函数要接收的参数个数、参数类型和参数顺序。
编译器用函数原型确定正确地调用了函数。
编译器忽略函数原型中提到的参数。
每个标准库都有对应的头文件,包含库中所有函数的函数原型和这些函数所需各种数据类型和常量的定义。
程序员可以生成和包括自定义的头文件。
通过按值调用传递参数时,参数值生成副本并传入被调用函数。副本的改变并不影响调用者的原始变量值。
rand函数产生0到RAND_MAX之间的整数,RAND_MAX的值至少应为32767。
rand和srand的函数原型在<stdlib.h>中。
通过比例缩放和移动,可以调整1+and()%6产生的整数值范围。
随机化是用标准库函数srand函数完成的。
srand语句只在程序完全调试之后才插入程序中。调试时最好省略srand语句,这样可以保证重复性.也是保证修改后的随机数产生程序正确工作的基础。
如果不想每次输入种子值而随机化.则要用如下语句srrnd(time(0)),使计算机通过时钟值自动取得种子值。time函数(上述语句中的参数0)返回当前“日历时间”的秒数。time函数的函数原型在<time.h>中。
可以将随机数结果一般化为如下形式:
n = a + rand( ) % b;
其中n是位移值(等于所要的连续整数范围的开始值),b是比例因子(等于由连续整数构成的该范围的宽度)。
枚举类型由关键字enum和类型名构成.是一组用户标识符表示的整数常量。
这些枚举常量的值从。开始.增量为1,但也可以指定其他值。
enum中的标识符必须惟一,但不同枚举常量可以取相同的值。
任何枚举常量可以在枚举定义中显式地赋给一个整数值。
每个标识符都包括存储类、作用域和连接属性。
C++提供了4个存储类说明符:auto、register、extern和static。
标识符的存储类确定标识符在内存中存在的时间。
标识符的作用域是程序中能引用这个标识符的区域。
标识符的连接确定多源文件程序中,只有当前源文件或是在任何正确声明的源文件中识别标识符。
自动存储类变量在进入声明该变量的程序块时生成,在程序块活动期间存在,在退出这个程序块时删除。函数的局部变量和参数通常是自动存储类。
存储类说明符register可以放在自动变量声明之前,让编译器在计算机的高速寄存器中保存这个变量。编译器可以忽略register声明。关键字register只能用于自动存储类的变量。
关键字extern和stattc声明静态存储类函数和变量的标识符。
程序开始执行时就分配和初始化静态存储类。
静态存储类有两种标识符:外部标识符与存储类说明符static中声明的局部变量。
全局变量生成时将变量声明放在任何函数定义之外,全局变量在整个程序执行期间保留其数值。
static局部变量在函数退出时保持其数值。
所有静态存储类的数字变量默认初始化为0,但也可以由程序员显式初始化。
一个标识符的4个作用域是函数范围、文件范围、块范围和函数原型范围。
标号是惟一具有函数范围的标识符。标号可以在所在函数中任何地方使用,但不能在函数体之外引用。
任何函数之外声明的标识符的作用域是文件范围。可以从声明该标识符处到文件末尾的任何函数中访问该标识符。
块中声明的标识符的作用域是块范围。块范围从标识符声明开始,到表示程序终止的右花括号(1)处结束。
函数开头声明的局部变量的作用域是块范围,函数参数也是,它们也是函数的局部变量。
任何块都可以包含变量声明。块嵌套时,如果外层块中的标识符与内层块中的标识符同名,则外层块中的标识符“隐藏”,直到内层块终止。
只有函数原型参数表中使用的标识符才具有函数原型范围。参数表中使用的标识符可以在程序中其他地方复用,不会产生歧义。
递归函数是直接调用自己或通过另一函数间接调用自己的函数。
如果函数调用基本情况,则只是返回一个值。如果在更复杂的问题中调用函数,则函数将问题分成两个概念性部分函数中能够处理的部分和函数中不能够处理的部分。由于这个新问题与原问题相似,因此函数运行递归调用处理这个较小的问晤。
要让递归最终停止,每次函数调用时都要使问题进一步简化,从而产生越来越小的问题,最终合并到基本情况。这时,函数能识别这个基本情况,将结果返回前一个函数调用,井回溯一系列结果,直到原函数调用最终返回最后的结果。
C++语言只指定四种运算符的操作数求值顺序,即&&、||、逗号运算符(,)和?:。前三种是二元运算符,操作数从左向右求值。第四种是C++惟一的三元运算符,先求值最左边的操作数,如果最左边的操作数非0,则求值中间的操作数,忽略最后的操作数:如果最左边 的操作数为0,则求值最后的操作数,忽略中间的操作数。
递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。
递归与迭代都涉及重复操作:迭代显式使用重复结构,而递归通过重复函数调用实现重复。
递归与迭代都涉及终止测试:迭代在循环条件失败时终止,遵归在遇到基本情况时终止。
迭代和递归都可以无限进行:如果循环测试永远不变成false,则迭代发生无限循环;如果递归永远无法归并到基本情况,则发生无穷递归。
递归重复调用机制,因此重复函数调用的开销很大,将占用很长的处理器时间和大量的内存空间。
如果要编译C++程序,就要求提供每个函数的函数原型或先定义再使用每个函数。
不返回数值的函数用void返回类型声明。想从这个函数返回一个值或在调用函效中使用这个函数调用的结果是个语法错误。
空参数表用空括号或void指定。
内联函数可以减少函数调用开销。关键字inline指示编译器在适当的时候将函数代码复制到程序中,以减少函数调用。编译器可以忽略内联函数的声明。
引用参数是C++提供的两种按引用调用的方法之一。要表示函数参数按引用传递,只要在函数原型中参数类型后面加上&。在函数调用中,只要指定变量名,即按引用调用传递该变量。在被调用函数中,通过参数名指定的变量实际上就是引用了调用函数中的原始变量,被调函数可以直接修改原始变量。
引用也可以用作函数中其他变量的别名。引用变量必须在声明中初始化,不能作为其他变量的别名而重新赋值。将引用声明为另一变量的别名后,对该别名(即引用)进行的所有操作实际上是对原始变量本身进行。
程序员可以提供默认参数的默认值。调用函数时如果省略默认参数,则采用该参数的默认值。如果省略的参数不是参数表中最右边的参数.则该参数右边的所有参数也应省略。默认参数应在函数名第一次出现时指定,默认值可以是常量、全局变量或函数调用。
一元作用域运算符可以在同名局部变量作用域中访问全局变量。
C++允许定义多个同名函数,这些函数采用不同参数集(至少有不同类型的参数)。这个功能称为函数重载。调用重载函数时,C++编译器通过检查调用中的参数个数、类型和顺序来选择相应的函数。
重载函数可以有不同的返回类型,但必须有不同参数表。生成不同返回类型和相同参数表的重载函数是个语法错误。
函数模板只定义一次,即可对不同类型数据生成进行相同操作的函数。