函数基础
一个典型的函数(function)定义包括以下部分:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。其中,形参以逗号隔开,形参的列表位于一对圆括号之内。函数执行的操作在语句块中说明,该语句块称为函数体.
书中使用了一个实现数的阶乘的简单函数用于举例说明:
//N的阶乘N*(N-1)*(N-2)...*((N-(N-1))*1)
int func(int N)
{
int nRes = 1; //局部变量用于保存计算结果
while(N > 1)
{
nRes *= N--;
}
return nRes; //返回结果
}
调用函数的方法也很简单,如下代码:
int main()
{
int nResult = func(5); //计算5的阶乘
cout << "5!is"<<nResult <<endl
}
函数的调用完成两项工作:
一是用实参初始化函数对应的形参,
二是将控制权转移给被调用函数。
此时,主调函数的执行被暂时中断,被调函数开始执行。
形参和实参
实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,以此类推。实参与形参存在对应关系,但是并没有规定实参的求值顺序,编译器能以任意可行的顺序对实参求值。
但是实参的类型必须与对应的形参类型匹配,函数有几个形参,我们就必须提供相同数量的实参。
如下面的 fact函数,此函数只有一个int类型的形参,所以每次我们调用它的时候都必须提供一个能转换成int的实参
fact ("hello"); //错误:实参类型不正确
fact (); //错误:实参数量不足
fact (42, 10, 0); //错误:实参数量过多
fact (3.14); //正确:该实参能转换成int类型
函数的形参列表
函数的形参列表有以下特点
1. 函数的形参列表可以为空
2. 任意两个形参都不能同名,而且函数最外层作用域中的局部变量也不能使用与函数形参一样的名字。
3. 形参名是可选的,但是由于我们无法使用未命名的形参,所以形参一般都应该有个名字。偶尔,函数确实有个别形参不会被用到,则此类形参通常不命名以表示在函数体内不会使用它。但是即便某个形参不被函数使用,也必须为它提供一个实参。
函数返回类型
大多数类型都能用作函数的返回类型。一种特殊的返回类型是void,它表示函数不返回任何值。
但是函数的返回类型不能是数组类型或函数类型,但可以是指向数组或函数的指针。
在C++语言中,名字有作用域,对象有生命周期,理解这两个概念非常重要。
1. 名字的作用域是程序文本的一部分,名字在其中可见。
2. 对象的生命周期是程序执行过程中该对象存在的一段时间。
如我们所知,函数体是一个语句块。块构成一个新的作用域,我们可以在其中定量。形参和函数体内部定义的变量统称为局部变量。它们对函数而言是"局部”的,仅在函数的作用域内可见,同时局部变量还会隐藏在外层作用域中同名的其他所有声明中。
自动对象
对于普通局部变量对应的对象来说,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。我们把只存在于块执行期间的对象称为自动对象(automatic object)。当块的执行结束后,块中创建的自动对象的值就变成未定义的了。形参就是一种自动对象。
局部静态对象
某些时候,我们需要局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象。局部静态对象(local static object)在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止方被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。
举个例子,说明局部静态对象的作用
size t count_calls ()
{
static size_t ctr =0; //调用结束后,这个值仍然有效
return ++ctr;
}
使用此函数:
int main()
{
for (sizeti =0; i!= 10; ++i)
{
cout << count _calls() << endl;return 0;
}
}
函数声明
和其他名字一样,函数的名字也必须在使用之前声明。类似于变量,函数只能定义一次,但可以声明多次。
函数的声明和函数的定义非常类似,唯一的区别是函数声明无须函数体,用一个分号替代即可。
因为函数的声明不包含函数体,所以也就无须形参的名字。事实上,在函数的声明中经常省略形参的名字。尽管如此,写上形参的
void print (vector<int>::const iterator beg,vectorcint>::const iterator end);
函数的三要素(返回类型、函数名、形参类型)描述了函数的接口,说明了调用该函数所需的全部信息,函数声明也称作函数原型(function prototype)。