1.4 函数
如果我们打算在C++程序中完成某些任务,最好的方式就是调用函数。要想准确描述某项操作的细节,把它定义成函数是最优选择。需要注意的是:函数必须先声明后使用。
一条函数声明语句需要完成三项任务:指定函数的名字、函数的返回值类型(如果有的话)以及要想调用该函数必须提供的实参数量和类型。例如:
https://yqfile.alicdn.com/7f25c9d853b741fbce9269a894177da354871aea.png
" >
在函数声明语句中,返回值类型位于函数的名字之前,实参类型则位于函数的名字之后,并且用括号括起来。
实参传递的过程与拷贝初始化非常类似,编译器负责检查实参的类型,并且在必要的时候执行隐式实参类型转换(见1.5节)。例如:
对于发生在编译过程中的类型检查和转换,程序员需要给予足够的重视。
我们可以在函数的声明语句中写上实参的名字,这有助于程序的读者理解该函数的含义。但事实上,除非该声明同时也是函数的定义,否则实参的名字不会影响编译过程。例如:
返回值类型和实参类型属于函数类型的一部分。对于类成员函数(见2.3节,4.2.1节)来说,类名字本身也是函数类型的一部分。例如:
每个程序员都希望自己编写的代码易于理解,因为易于理解是提高代码可维护性的第一步。而要使得整个程序易于理解,首先要把复杂的计算任务分解到易于理解的若干个模块中(以函数和类的形式),并给这些模块起个通俗易懂的名字。函数组成了计算的基本词汇表,正如类型(包括内置类型和用户自定义类型)组成了数据的基本词汇表。C++标准算法(如find、sort和iota)是程序函数化的良好开端(见第10章),接下来我们就能用这些表示通用任务或者特殊任务的函数组合出更复杂的计算模块了。
代码中错误的数量通常与代码的规模和复杂程度密切相关,多使用一些更短小的函数有助于降低代码的规模和复杂度。举例来说,通过把一项专门的任务定义成函数,我们就能在别的代码段内节省出空间,从而使程序的逻辑结构更加清晰易懂;同时我们也就不得不为这些任务命名并明确它们的依赖关系。
如果程序中存在名字相同但实参类型不同的函数,则编译器负责为每次调用选择匹配度最高的函数。例如:
https://yqfile.alicdn.com/e304e63da27a6d7a55b5c2f77ce3c722ad51b10b.png
" >
如果存在两个可供选择的函数并且它们难分优劣,则编译器认为此次调用具有二义性并报错。例如:
https://yqfile.alicdn.com/ad01b131355ce7ce9fedd091b1fe05ea844d4bb1.png" >
上述关于同名函数的规定就是我们所熟知的函数重载,函数重载是泛型编程(见5.4节)的一个关键问题。如果函数被重载了,则所有重载的同名函数应该实现相同的语义内容。print()函数符合这一规定,每个print()函数都会把它的实参打印出来。