目录
2.4 函数
由于函数用于创建C++程序模块,对C++的OOP定义至关重要,因此必须要熟悉它。函数的某些方面属于高级主题,将在后面的章节详细介绍。
C++函数分为两种:有返回值的和没有返回值的。你可以在标准C++函数库中可以找到这两类函数,也可以自己创建这两种类型的函数。
2.4.1 使用有返回值的函数
有返回值的函数将生成一个值,这个值可以赋值给变量或在其他表达式中使用。例如C/C++库中包含一个名为 sqrt() 的函数,它返回平方根。假设要计算 6.25 的平方根,并将值赋给 x,那么可以使用如下语句:
x = sqrt(6.25);
表达式 sqrt(6.25) 将调用 sqrt() 函数。表达式 sqrt(6.25) 被称为函数调用,被调用的函数叫做被调用函数(called function),包含函数调用的函数叫做调用函数(calling function)。
上式中圆括号里的值是发送给函数的信息,这被称为传递给函数。以这种方式发送给函数的值叫做参数。函数 sqrt(6.25) 得到的结果是 2.5,并将这个值发送给调用函数,发送回去的值叫做返回值(return value)。换句话说,在函数运行完之后,语句中的函数调用部分(sqrt(6.25))将被替换为返回值(2.5),而 2.5 又被赋值给 x。
在使用函数之前,需要让编译器知道函数的参数类型和返回值类型。如果缺少这些信息,编译器将不知道如何解释返回值。C++提供这种信息的方式是使用函数原型语句,并且C++程序应当为程序中使用的每一个函数提供函数原型。
C++库将 sqrt() 函数定义为将一个可能带小数部分的数字作为参数,并返回一个相同类型的数字。这种可能带小数的数字被称为实数,但是在C++中称这种数为 double。函数 sqrt() 的原型:
double sqrt(double);
第一个 double 意味着函数 sqrt() 会返回一个 double 类型的值。括号中的 double 表示函数sqrt() 需要一个 double 类型的参数。
原型结尾的分号表示他是一条语句,这使得他是一个原型,而不是一个函数头。如果省略分号,那么编译器将把这行代码解释为一个函数头,并接着要求提供描述函数的函数体。
在程序中使用 sqrt() 时也必须提供原型。可以用两种方法来实现:
1. 在源代码文件中输入函数原型;
2. 包含头文件 cmath(或 math.h),其中定义了原型。
推荐使用第二种方式。对于C++库中的每个函数,都在一个或多个头文件中提供了原型。在使用C++库中的相关函数时,请务必引用(include)包含该函数原型的头文件。
下面是正确使用函数 sqrt() 的实例程序:
//程序清单2.4
#include <iostream>
#include <cmath> //或 math.h
using namespace std;
//程序利用正方形面积计算正方形边长
int main()
{
double area; //定义变量area存储正方形面积
double side; //定义变量side存储正方形边长
cout << "请输入正方形的面积:" << endl;
cin >> area; //利用cin从键盘中读取输入的正方形面积
side = sqrt(area); //使用函数计算正方形边长,并将值赋给side
cout << "正方形的边长为:" << side << endl;
}
运行结果:
请输入正方形的面积:
2.25
正方形的边长为:1.5
注意:
1. 不可以将变量 area 声明为 int、char等类型的变量。即使你想要输入的面积是 1600,是一个整数。因为如果输入的 area 为整型变量,那么传入 sqrt() 的参数就是 int 类型的,这是不允许的(函数原型的限制)。
2. 不可以将有返回值的函数应用于如下语句(以 sqrt() 为例):
sqrt(2.25);
这很好理解:sqrt(2.25) 将返回一个 double 类型的值,但是这个值即不处于一个合法的表达式中,也不为变量赋值。这样写与如下语句无非:
1.5;
这是不被允许的。
2.4.2 函数变体
有些函数需要多项信息。这些函数使用多个参数,参数间使用逗号分开。例如:函数 pow() 接受两个参数,返回以第一个参数为底,以第二个参数为指数的幂。该函数的原型:
double pow(double, double);
如果要计算2的16次方,可以使用如下语句(需要包含头文件):
answer = pow(2.0, 16.0);
另外有一些函数不使用任何参数。例如:有一个C库(与cstdlib或stdlib.h头文件相关的库)包含一个 rand() 函数,该函数不接受任何参数,并返还一个随机的整数。该函数原型为:
int rand(void);
关键字 void 指出该函数不接受任何参数。如果省略 void 让括号为空,那么这是以一个不接受参数的隐式声明。
如果要获取一个整数随机值,可以使用如下语句(需要包含头文件):
random_number = rand();
还有一些函数没有返回值。例如:sort() 是一个排序函数,他就没有返回值。其函数原型为:
sort(first_pointer, first_pointer+n, cmp);
在有些语言中,有返回值的函数被称为函数(function);没有返回值的函数被称为过程(procedure)或子程序(subroutine)。但是C/C++中一致,两种变体都成为函数。
2.4.3 用户定义的函数
每一个C++程序都必须要有一个 main() 函数,用户必须对它进行定义。假设需要添加另一个用户定义的函数。和库函数一样,也可以通过函数名来调用用户定义的函数。对于库函数,使用之前必须要提供其原型,通常把原型放到 main() 函数定义之前。但现在我们要自己定义函数,就要自己提供函数的源代码。最简单的方法,就是将函数放在 main() 函数之后。
//程序清单2.5
#include <iostream>
//用户自定义函数的原型
void Speak(int years);
int main() {
using namespace std;
int age = 20;
int years;
Speak(20); //调用Speak()函数
cout << "请输入又过了多少年:" << endl;
cin >> years;
age = age + years;
Speak(age); //再次调用Speak()函数
return 0;
}
//用户定义的函数,具体实现放在main()函数后面
void Speak(int number){
using namespace std;
cout << "我今年" << number << "岁了。" << endl;
}
运行结果:
我今年20岁了。
请输入又过了多少年:
8
我今年28岁了。
main() 函数两次调用 Speak() 函数,第一次的参数是 20,第二次的参数是变量 age,其值为20+8 = 28。在两次的函数调用之间,通过用户输入的一个数字来调整了函数 cout 的内容。
1. 函数格式
程序清单2.5中函数 Speak() 与主函数 main() 的定义采用相同的格式,一个函数头,一个话括号中的函数体:
return_type function_name(argumentlist)
{
statements;
}
2. 函数头
Speak() 函数的函数头如下:
void Speak(int number)
关键字 void 指出函数 Speak()没有返回值。因此调用 Speak() 不会产生任何可用于赋值或运算的数字或字符等。回看 main() 函数,它为什么会有返回值呢?它的返回值给谁使用?难道程序里还有调用 main() 函数的函数吗?
答案是,可以将计算机的操作系统(如 UNIX 或 Windows)看作调用程序。因此,main() 函数的返回值并不是返回给程序的其他部分,而是返回给操作系统。很多操作系统都可以使用程序的返回值。
关键字:关键字是计算机语言中的词汇。本章使用了 int、double、void 以及 return 等关键字。由于这些关键字都是C++专用,我们不能使用它去用作变量名,或者是函数名,但是可以包含它。例如:point 中包含 int 但是可以用作变量名和函数名。
2.4.4 用户定义的具有返回值的函数
我们再来编写一个具有返回值的函数程序:
//程序清单2.6
#include <iostream>
//用户自定义函数的原型
int Addition(int a, int b);
int main() {
using namespace std;
int first;
int second;
int add;
cout << "请输入两个正整数:" << endl;
cin >> first;
cin >> second;
//计算first和second的和,并将值赋给add
add = Addition(first, second);
cout << "这两个数的和为:" << add << endl;
return 0;
}
//计算传入的两个参数的和
int Addition(int a, int b){
return a + b;
}
运行结果:
请输入两个正整数:
1 2
这两个数的和为:3
上述程序就非常好理解了。输入的两个正整数分别存储在 first 和 second 两个变量中,再将这两个变量传入函数中(传入的是形参,后面章节会介绍),函数计算和之后再将和的值返回个调用函数。
虽然函数 Addition() 很短,但是它包含了函数的所有特点:
1. 有函数头和函数体。
2. 接受一个参数。
3. 返回一个值。
4. 需要一个原型。
2.4.5 在函数程序中使用using编译指令
例如,程序清单2.5中分别在 main() 函数和 Speak() 函数的函数体中使用:
using namespace std;
当前通行的理念是,只让需要访问名称空间 std 的函数访问它是更好的选择。