C++学习——第9章 函数

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);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值