1 函数的重载
利用函数的重载,可以在一个程序中使用同名的若干个函数。主要的限制是给定名称的每个函数必须有不同的参数列表。
如果满足下列条件之一,两个同名函数就是不同的:
· 参数的个数相同,但至少有一对对应参数的类型不同 |
1.1 函数的签名
函数的名称及其参数类型组合在一起,就定义了一个惟一的特性,称之为函数签名。
函数签名可以区分不同的函数,所以程序中的每个函数都必须有惟一的函数签名。只要在程序中调用函数,就必满足这个条件。
注释:
返回类型不是函数签名的一部分。实际上,这是符合逻辑的,在调用函数时,不需要存储返回的值,因此函数的返回类型就不必由调用语句确定。
代码:重载一个函数
#include <iostream>
using namespace std;
double larger(double a, double b);
longlarger(long a, long b);
int main()
{
double a_double = 1.5, b_double = 2.5;
float a_float = 3.5f, b_float = 4.5f;
long a_long = 15L, b_long = 25L;
cout << endl;
cout <<"The larger of double values "
<<a_double<<" and "<<b_float<<" is "
<<larger(a_double, b_double)<<endl;
cout<<"The larger of float values"
<<a_float<<" and "<<b_float<<" is "
<<larger(a_float, b_float)<<endl;
cout<<"The larger of long values"
<<a_long<<" and "<<b_long<<" is "
<<larger(a_long, b_long)<<endl;
return 0;
}
double larger(double a, double b)
{
cout << "double larger() called" <<endl;
return a>b ? a : b;
}
long larger(long a, long b)
{
cout << "long larger() called"<<endl;
return a>b ? a : b;
}
其中,代码
cout<<"The larger of float values" <<a_float<<" and "<<b_float<<" is " <<larger(a_float, b_float)<<endl; |
调用了参数类型为float的函数larger()。我们没有定义这样的函数,但是,编译器可以把float转换为double,且没有数据丢失;于是,这是一个可接受的自动转换,编译器可以使用接受double参数的larger()版本。
1.1 重载和指针参数
由于指向不同类型的指针是不同的,因此下面的原型声明了两个不同的重载函数:
int larger(int* pValue1, int* pValue2); int larger(float* pValue1, float* pValue2); |
可以使用指向给定类型的指针作为参数。
注意:它的解释方式与该类型的数组相同。比如,int*类型的参数处理起来与int[]的参数类型相同。
下面的原型声明了相同的函数,而不是两个不同的函数:
int largest(int values[], int count); int largest(int* values, int count); |
指定这两种参数类型中的任何一种,所传送的参数都是地址,可以使用数组表示法或指针表示法来实现该函数。
1.2 重载和引用参数
不能把参数是给定类型data_type的函数,重载为参数类型是“引用data_type”的函数。否则,编译器就不能确定要调用哪个函数。
下面声明两个函数原型:
int do_it(int number); int& do_it(int& number); |
假定value的类型是int,则下面的语句:
do_it(value); |
就可以调用这两个函数中的任何一个。无法区分应调用哪个函数,因此不能根据一个版本的参数是给定类型,另一个版本的参数是该类型的引用,来区分重载函数。
代码:重载带有引用参数的函数
#include <iostream>
using namespace std;
double larger(double a, double b);
long& larger(long&a, long& b);
int main()
{
double a_double = 1.5, b_double = 2.5;
cout<<endl;
cout<<"The larger of double values "
<<a_double<<" and "<<b_double<<" is "
<<larger(a_double, b_double)<<endl;
int a_int = 35, b_int = 25;
cout<<"The larger of int values "
<<a_int<<" and "<<b_int<<" is "
<<larger(static_cast<long>( a_int), static_cast<long>(b_int))
<<endl;
return 0;
}
double larger(double a, double b)
{
cout<<"double larger() called."<<endl;
return a>b ? a : b;
}
long& larger(long& a, long& b)
{
cout <<"long ref larger() called."<<endl;
return a>b ? a : b;
}
第二个输出结果并不是我们期望的结果,我们希望第二个输出语句调用带有long&参数的larger()版本:
cout<<"The larger of int values " <<a_int<<" and "<<b_int<<" is " <<larger(static_cast<long>( a_int), static_cast<long>(b_int)) <<endl; |
而本例中的语句调用带有double参数的函数,为什么?我们已经把两个参数都强制转换为long了!
实际上,这既是问题所在,参数不是a_int和b_int,而是包含相同值的临时位置,这两个值转换为long类型。在幕后,编译器没有准备使用临时地址来初始化引用,这太冒险了。larger()中的代码可以自由控制它对引用参数进行的操作,在理论上,两个引用参数都可以修改和返回。因为以这种方式使用临时位置不是很明智,所以编译器不使用。
处理方法有两种:1.把a_int和b_int声明为long类型。编译器就会调用参数类型为”引用long”的larger()版本。
2.如果环境不允许第一条这么做,还可以把引用参数声明为const:
long larger(const long& a, const long& b); |
一定要在函数原型和函数定义中同时进行修改,同志编译器函数不能修改参数,于是编译器就允许调用这个版本,而不是参数为double的版本。
1.1 重载和const参数
const仅能用于在定义函数签名时,区分是为引用定义参数,还是为指针定义参数。对于基本类型(如int),从重载的观点来看,const int 与 int是相同的。因此,下面的原型具有相同的函数签名,并声明同一个函数:
long& larger(long a, long b); long& larger(const long a, const long b); |
编译器会忽略第二个声明中的参数的const,这是因为参数是按值传送的,也就是说,会把每个参数的副本传送给函数,函数不会修改参数的初始值。
注意:对于任何基本类型T,参数类型是const T的函数都会解释为参数类型为T的重载函数,const会被忽略。
1.2 重载和默认参数值
对于重载函数,指定默认参数值有时会影响编译器区分函数调用的能力,产生不确定的结果。
2. 函数模板
在上面描述的一些情形中,我们编写了包括相同代码的重载函数。似乎没有必要重复编写相同的代码,最好代码只编写一次,即采用函数模板的方式。
函数模板是函数的蓝图或厨房,编译器使用它生成函数系列的新成员。
函数模板的开头是关键字template,表示这是一个模板。其后是一对尖括号,它包含了参数列表。
例如:
template<class T>T larger(T a, T b) { return a>b ? a : b; } |
单词class是一个关键字,它表示T是一个类型,但是不表示T必须是类类型。T可以使基本类型,如Int或long,也可以是用户定义的类型(也就是类)。
注释:在模板定义中,尖括号中可能会包含关键字typename,这是关键字class的另一个常见名称,与class有相同的作用。
在代码中编写模板就像编写征程的函数定义一样:
template<class T>T larger(T a, T b); |
在使用从模板中生成的函数之前,必须确保把声明(即原型)或模板的定义放在源文件中。
代码:使用函数模板
#include <iostream>
using namespace std;
template<class T>T larger(T a, T b);
int main()
{
double a_double = 1.5, b_double = 2.5;
cout<<endl;
cout<<"The larger of double values "
<<a_double<<" and "<<b_double<<" is "
<<larger(a_double, b_double)<<endl;
int a_int = 35, b_int = 25;
cout<<"The larger of int values "
<<a_int<<" and "<<b_int<<" is "
<<larger(static_cast<long>( a_int), static_cast<long>(b_int))
<<endl;
return 0;
}
template<class T>T larger(T a, T b)
{
return a>b ? a : b;
}
3. 指针
指针存储了一个地址值。指针也可以指向函数的地址。在程序执行过程中,这种指针可以在不同的时候指向不同的函数。在程序中,总是可以使用指针来调用函数,被调用的函数地址是最近赋予指针的。
提示:函数指针在C++中没有C中那样普遍。这是因为C++提供了其他能完成类似任务的功能(类、函数重载等)。但是,函数指针在C++语言中仍有重要的地位。
3.1 声明函数指针
下面声明一个指针pfun,用于指向一个函数,该函数带有两个参数,其类型分别是long*和int,其返回值的类型是long。该声明如下所示:
long(*pfun)(long*, int); |
所有实体都加上了括号。
提示:指针名pfun的括号和星号是必须有的,没有它们,这个语句声明的就是函数,而不是指针,因为*会加在long上,而不是pfun上。
声明函数指针的一般形式:
返回类型(*指针名)(参数类型列表); |
代码:函数指针
#include<iostream>
using namespace std;
long sum(long a, long b);
long product(long a, long b);
int main()
{
long (*pDo_it)(long, long) = 0;
pDo_it = product;
cout << endl
<< "3*5 = "
<< pDo_it(3, 5);
pDo_it = sum;
cout << endl
<< "3*(4+5)+6= "
<< pDo_it(product(3, pDo_it(4, 5)), 6);
cout << endl;
return 0;
}
long product(long a, long b)
{
return a*b;
}
long sum(long a, long b)
{
return a+b;
}
3.2 把函数作为参数传送
代码:传送函数指针
#include<iostream>
using namespace std;
double squared(double);
double cubed(double);
double sum_array(double array[], int len, double (*pfun)(double));
int main()
{
double array[]={1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5};
int len = sizeof array/sizeof array[0];
cout << endl
<< "Sum of squares = "
<< sum_array(array, len, cubed)
<< endl;
cout << "Sum of cubes ="
<< sum_array(array, len, cubed)
<< endl;
return 0;
}
double squared(double x)
{
return x*x;
}
double cubed(double x)
{
return x*x*x;
}
double sum_array(double array[], int len, double(*pfun)(double))
{
double total = 0.0;
for(int i=0; i<len; i++)
total += pfun(array[i]);
return total;
}
3.3 函数指针的函数
声明指针数组的例子:
double sum(double, double); double product(double, double); double difference(double, double); double (*pfun[3]) (double, double) = { sum, product, difference}; |
4. 递归
代码:递归函数
#include<iostream>
#include<iomanip>
using namespace std;
double power(double x, int n);
int main()
{
cout << endl;
for(int i = -3; i <= 3; i++)
cout << setw(10) << power(8.0, i);
cout << endl;
return 0;
}
double power(double x, int n)
{
if(0 == n)
return 1.0;
if(0 < n)
return x*power(x, n-1);
return 1.0/power(x, -n);
}