内置函数/函数的重载/函数模板/有默认参数的函数

参考数目:《C++程序设计(第3版)》谭浩强 编著(清华大学出版社)

  • 上一篇文章整理过第1章的内容,第2章和第3章的大部分内容我在"C语言"专栏中整理过,需要的可以去翻翻,毕竟C++是在C语言的基础上改进的,保留了很多C语言原有的内容,在此我就不重复整理了。
  • 带星号的部分,也就是红框的部分,它们是C++对C语言的发展,是C语言中没有的,因此第2章中的内容参考C语言即可。
  • 第3章红框的部分主要是C++与C语言输入和输出的区别。C语言中使用scanf和printf进行输入合输出,而C++中则使用cin和cout进行输入和输出。另外C++中提供了C语言没有提供的逻辑数据,逻辑常量只有两个,即false(假)和true(真)。逻辑型变量要用类型标识符bool来定义,它的值只能是true和false之一。
  • 第4章增加的内容比较多,因此这篇文章主要整理一下 内置函数、函数的重载、函数模板、有默认参数的函数。对于第4章的其他内容,之前也在"C语言"专栏中整理过了。



第4章 利用函数实现指定的功能

内置函数

  • 调用函数时需要一定的时间和空间的消耗,一般的程序调用函数时的过程:
  1. 程序先执行函数调用之前的语句;
  2. 程序的控制转移到被调用函数的入口处,同时进行参数传递;
  3. 执行被调用函数中函数体的语句;
  4. 流程返回调用函数的下一条指令处,将函数返回值带回;
  5. 接着执行主调函数中未执行的语句。
  • 这样就要求在转去被调用函数之前,要记下当时执行的指令的地址,还要"保护现场"(记下当时有关的信息),以便在函数调用之后继续执行。在函数调用之后,流程返回到先前记下的地址处,并且根据之前记录的信息"恢复现场",然后继续执行。这些都要花费一定的时间。如果有的函数需要频繁使用,则所需要的时间就会很长,从而降低程序的执行效率。有些实用程序对效率是有要求的,要求系统的响应时间短。这就希望尽量压缩时间的开销。
  • C++提供了一种提高效率的方法,即在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。这种嵌入到主调函数中的函数称为内置函数(inline function)。
  • 指定内置函数的方法很简单,只需在函数首行的左端加一个关键词inline即可。

例1:

#include <iostream> //预处理指令
using namespace std; //在函数之外的全局声明
inline int max(int, int, int); //声明内置函数,注意左端有inline
int main() //函数首部
{
	int i = 10, j = 20, k = 30, m;
	m = max(i, j, k);
	cout << "max =" << m << endl;
	getchar();
	return 0;
}
inline int max(int a, int b, int c) //定义max为内置函数
{
	if (b > a) a = b; //求a,b,c中的最大值
	if (c > a) a = c;
	return a;
}

程序分析:

  • 由于在定义函数时指定它为内置函数,因此编译系统在遇到函数调用m = max(i, j, k);时,就用max函数体的代码代替max(i, j, k),同时将实参代替形参。这样,程序第6行就被替换成
if (j > i) i = j;
if (k > i) i = k;
m = i;
  • 注意:可以在声明和定义函数时同时写inline,也可以只在函数声明时加inline,而定义函数时不加inline。只要在调用函数之前把inline的信息告知编译系统,编译系统就会在处理函数调用时按内置函数处理。
  • 使用内置函数可以节省运行时间,但增加了目标程序的长度。因此只将规模很小(一般为5个语句以下)而使用频繁的函数(如定时采集数据的函数)声明为内置函数。在函数规模很小的情况下,函数调用的时间开销可能相当于甚至超过执行函数本身的时间,把它定义为内置函数,可大大减少程序运行时间。
  • 内置函数中不能包括复杂的控制语句,如循环语句和switch语句。归纳起来,只有那些规模较小而又被频繁调用的简单函数,才适合于声明为inline函数。

函数的重载

  • 在编程时,一般是一个函数对应一种功能。但有时我们要实现的是同一类的功能,只是有些细节不同。例如希望从3个数中找出其中的最大值,而每次求最大数时数据的类型不同,可能是3个整数、3个双精度数或3个长整数。程序设计者会分别设计出3个不同名的函数,其原型为
int max1(int a, int b, int c); //求3个整数中的最大数
double max2(double a, double b, double c); //求3个双精度数中的最大数
long max3(long a, long b, long c); //求3个长整数中的最大数
  • 以上3个函数的函数体是相同的。程序要根据不同的数据类型调用不同名的函数。如果在一个程序中这类情况较多,对程序编写者来说,要分别编写出功能相同而名字不同的函数,这是很不方便的。
  • C++允许用同一函数名定义多个函数,而这些函数的参数个数和参数类型可以不相同,这就是函数的重载(function overloading)。即对一个函数名重新赋予它新的含义,使一个函数名可以多用。所谓重载,其实就是"一物多用"

例2:求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数重载方法

#include<iostream>
using namespace std;
int main()
{
	int max(int a, int b, int c); //函数声明
	double max(double a, double b, double c); //函数声明
	long max(long a, long b, long c); //函数声明
	
	int i1, i2, i3, i;
	cin >> i1 >> i2 >> i3; //输入3个整数
	i = max(i1, i2, i3); //求3个整数中的最大值
	cout << "i_max=" << i << endl;

	double d1, d2, d3, d;
	cin >> d1 >> d2 >> d3; //输入3个双精度数
	i = max(d1, d2, d3); //求3个双精度数中的最大值
	cout << "d_max=" << d << endl;

	long g1, g2, g3, g;
	cin >> g1 >> g2 >> g3; //输入3个长整数
	g = max(g1, g2, g3); //求3个长整数中的最大值
	cout << "g_max=" << g << endl;
}

int max(int a, int b, int c) //定义求3个整数中的最大数的函数
{
	if (b > a) a = b;
	if (c > a) a = c;
	return a;
}

double max(double a, double b, double c) //定义求3个双精度中的最大数的函数
{
	if (b > a) a = b;
	if (c > a) a = c;
	return a;
}

long max(long a, long b, long c) //定义求3个长整数中的最大数的函数
{
	if (b > a) a = b;
	if (c > a) a = c;
	return a;
}

程序分析:

  • 可以看到,用一个函数名max分别定义了3个函数。系统会根据调用函数时给出的信息去找与之匹配的函数。上面的main函数3次调用max函数,而每次实参的类型不同,系统就根据实参的类型找到与之匹配的函数,然后调用该函数。
  • 上例3个max函数的函数体是相同的,其实重载函数并不要求函数体相同。重载函数除了允许参数类型不同以外,还允许参数的个数不同。
#include<iostream>
using namespace std;
int main()
{
	int max(int a, int b, int c); //函数声明
	int max(int a, int b); //函数声明
	int a = 8, b = -12, c = 27;
	cout << "max(a, b, c)=" << max(a, b, c) << endl; //输出3个整数中的最大值
	cout << "max(a, b)=" << max(a, b) << endl; //输出2个整数中的最大值
}
int max(int a, int b, int c) //此max函数的作用是求3个整数中的最大数
{
	if (b > a) a = b;
	if (c > a) a = c;
	return a;
}
int max(int a, int b) //此max函数的作用是求2个整数中的最大数
{
	if (a > b) return a;
	else return b;
}

程序分析:

  • 两次调用max函数的参数个数不同,系统就根据参数的个数找到与之匹配的函数调用它。
  • 参数的个数和类型可以都不同。但不能只有函数的类型不同而参数的个数和类型相同。
  • 重载函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同,函数返回值类型可以相同也可以不同。

函数模板

  • 函数的重载可以实现一个函数名多用,将实现相同的或类似的功能的函数用同一个函数名来定义。这样使编程者在调用同类函数时感到含义清楚,方法简单。但是在程序中仍然要分别定义每一个函数,为了解决这个问题,C++提供了函数模板(function template)。所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只须在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。

例3:

#include<iostream>
using namespace std;
template<typename T>
T max(T a, T b, T c)
{
	if (b > a) a = b;
	if (c > a) a = c;
	return a;
}
int main()
{
	int i1 = 185, i2 = -76, i3 = 567, i;
	double d1 = 56.87, d2 = 90.23, d3 = -32114.78, d;
	long g1 = 67854, g2 = -912456, g3 = 673456, g;
	i = max(i1, i2, i3); //调用模板函数,此时T被int取代
	d = max(d1, d2, d3); //调用模板函数,此时T被double取代
	g = max(g1, g2, g3); //调用模板函数,此时T被long取代
	cout << "i_max=" << i << endl;
	cout << "d_max=" << d << endl;
	cout << "i_max=" << i << endl;
	return 0;
}

定义函数模板的一般形式为:

template<typename T>
通用函数定义

或

template<class T>
通用函数定义
  • template的含义是"模板",尖括号中先写关键字typename(或class),后面跟一个类型参数T,这个类型参数实际上是一个虚拟的类型名,表示模板中出现的T是一个类型名,但是现在并未指定它是哪一种具体的类型。在函数定义时用T来定义变量a,b,c,显然变量a,b,c的类型也是未确定的。要等到函数调用时根据实参的类型确定T是什么类型。其实也可以不用T而用任何一个标识符,许多人习惯用T(T是Type的第一个字母),而且用大写,以与实际的类型名相区别。
  • class和typename的作用是相同的,都表示"类型名",二者可以互换。以前的C++程序员都用class。typename是之后被加到标准C++中的,因为用class容易与C++中的类混淆。而用typename的含义很清楚,是类型名(而不是类名)。
  • 可以看到,用函数模板比函数重载更方便,程序更简洁。但应注意它只适用于函数体相同、函数的参数个数相同而类型不同的情况,如果参数的个数不同,则不能用函数模板。

有默认参数的函数

  • 一般情况下:在函数调用时形参从实参那里取得值,因此实参的个数应与形参相同。但是有时多次调用同一函数时用的是同样的实参值,C++提供了简单的处理办法,给形参一个默认值,这样形参就不必一定要从实参取值了。如有一函数声明:float area(float r=6.5);。指定r的默认值为6.5,如果在调用此函数时,确认r的值为6.5,则可以不必给出实参的值,如:area(); //相当于area(6.5);。如果不想使形参取此默认值,则通过实参另行给出。如area(7.5); //形参得到的值为7.5,而不是6.5。这种方法比较灵活,可以简化编程,提高运行效率。
  • 如果有多个形参,可以使每个形参有一个默认值,也可以只对一部分形参指定默认值,另一部分形参不指定默认值。实参与形参的结合是从左至右顺序进行的,因此指定默认值的参数必须放在形参表列中的最右端,否则出错。在调用有默认参数的函数时,实参的个数可以与形参不同,实参未给定的,从形参的默认值得到值。利用这一特性,可以使函数的使用更加灵活。
  • 在使用带有默认参数的函数时有两点要注意:
  1. 如果函数的定义在函数调用之前,则应在函数定义中给出默认值。如果函数的定义在函数调用之后,则在函数调用之前需要有函数声明,此时必须在函数声明中给出默认值,在函数定义时可以不给出默认值。也就是说必须在函数调用之前将默认值的信息通知编译系统。由于编译是从上到下逐行进行的,如果在函数调用之前未得到默认值信息,在编译到函数调用时,就会认为实参个数与形参个数不匹配而报错。
    如果在声明函数时已对形参给出了默认值,而在定义函数时又对形参给出默认值,有的编译系统会给出"重复指定默认值"的报错信息,有的编译系统对此不报错,甚至允许在声明时和定义时给出的默认值不同,此时编译系统以先遇到的为准。由于函数声明在函数定义之前,因此以声明时给出的默认值为准,而忽略定义函数时给出的默认值。
  2. 一个函数不能既作为重载函数,又作为有默认参数的函数。因为当调用函数时如果少写一个参数,系统无法判定是利用重载函数还是利用默认参数的函数,会出现二义性,系统无法执行。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值