第九天(函数进阶 · 二)
来源:互联网 发布:网络接口 编辑:程序博客网 时间:2024/05/17 06:26
后半部分费时较多,因为很多内容都是新的。昨晚写完之时已是23:15,来不及编辑排版了,今早上传。
2011-10-11(Adventures In Functions II)
1、默认参数(default arguments)。C++提供设置默认参数的功能,当程序中摸个参数缺失时,自动使用该默认参数。如有一个add()函数,功能是将两个数相加后将结果返回,如果程序中这样调用:
int a = add(5);
缺失了第二个参数,而如果这是事先设置了第二个参数为0,则返回的是5+0 = 5。如此减少不必要的麻烦。要设置默认参参数,要从函数原型下手:
int add(int a, int b = 0);
这样,当程序中部提供第二个参数时,b = 0。有参数是会覆盖默认值。设置默认参数有个原则:要位某个参数设置默认参数,它的右边所有参数都要提供默认值,即如下情况是不合法的:
int ksyou(int a, int b = 4, int c);
有此规定是避免出现下面状况:
ksyou(5 , , 6);
这么下不是摆明程序是特意不写参数的么?违背了设置这个功能初衷。
2、函数重载(Function Overloading)。
①如下函数视为同一函数:
double doubleValue(double x);
double doubleValue(double& x);
②返回类型不能作为区别函数的依据:
double create(int, double);
int create(int, double); //this two guys are incompatible
③参数是否有const可作为依据,但仅限于引用和指针类型参数:
void print(char*); //overloadedvoid print(const char*); //overloadedconst char str1[10] = "C++ test";char char str2[10] = "C++ test";print(str1); //print(const char*)print(str2); //print(char*)④模棱两可的情况不允许:
int add(int a, int b, int c = 5);
int add(int a, int b);
如果主函数中如此调用:add(5,6);如此无法确定调用的是哪个函数。
3、函数模板(Function Templates)。试想,如果上述函数add()函数中的参数和返回类型改成double或long,是否每一次的需求都要重新编写。C++中,这是不需要的,因为有函数模板。简单的说是根据某个算法编写成一个模板,变的只是算法里面的数据类型。
使用关键字template,大概形式如下(以add()函数为例):
template <class/teyename T>
T add(T a, T b){ return a + b; }
上面,class和typename均为关键字,可使用任意一个。T可以为任意合法名字。今后,程序会根据参数类型,将根据这模板来具体化函数,如:
int a = b =5;add(a, b); //return 10double x = y = 5.36;add(x, y); //return 10.72再举一例:
#include <iostream>const int ArrSize = 5;using namespace std;template <class T>T max5(T [], int);int main(){ int intType[ArrSize] = {56,56,-54,0,-3001}; double doubleType[ArrSize] = {56.1,-65.64,210.3,100,-1.5}; cout << max5(intType, ArrSize) << endl; cout << max5(doubleType, ArrSize) << endl; return 0;}template <typename Any>Any max5(T a[], int size){ Any max = a[0]; for(int i = 0; i< ArrSize; i++) if(a[i] > max) max = a[i]; return max;}4、模板的重载。模板也可以重载,不过注意要定义可共存的函数,即函数的特征标要不同。如:
template <class T>
T add(T, T);
和
template <class Any>
Any add(Any ,Any );
视为视为同一模板;
template <class T>
T add(T, T);
和
template <class Any>
Any add(Any [],Any []);
视为视为不同模板;
5、模板的显式具体化。在C++中,如果是按值传递,结构体与基本数据类型无异。如此问题就出来了,结构体是有“内涵”的,模板的一般化处理可能不适用与结构体。比如,定义一个模板函数show()用作打印传过去参数的信息,显然,无法从广泛意义上打印所有参数类型信息。所以有模式具体化一说。
#include <iostream>#include <string>using namespace std;struct Student{ string name; int number;};template <class T>void show(T);void show(Student);template <> void show<Student>(Student);int main(){ Student stu= {"Tom", 23}; int a = 10; show(a); show(stu); return 0;}template <class T>void show(T a){ cout << a << endl;}void show(Student stu){ cout << "Name: " << stu.name << "; Number: " << stu.number << endl; cout << "Type1" << endl;}template <> void show<Student>(Student stu){ cout << "Name: " << stu.name << "; Number: " << stu.number << endl; cout << "Type2" << endl;}10
Name: Tom; Number: 23
Type1
12行,可简写成:template <> void show(Student);在函数前加上template <>即可将其具体化,并且重新编写代码。当然也可以更加具体地定义一个普通函数,专门用于打印结构体信息。如此出现了三类函数:非模板函数、模板函数和具体化的原型。它们的优先级顺序是:非模板函数>具体化的原型>模板函数。
6、深入理解和术语。模板是一个生成函数的方案,非定义。编译器使用模板根据需要生成特定函数定义,这个函数的定义称为实例(instantiation),这个是过程称为实例化(区别于之前的具体化)。实例化分隐式实例化(implicit instantiation)和显式实例化(explicit instantiation)。隐式实例化是编译器自动完成的。显式实例化可用下面语句进行:
template void show<double>(double);
这个语句写在主函数中,不需要重新编写代码,这是编译器根据模板生成的函数,已经是一个实例了。以后如果有:
double a;
show(a);
编译器将直接调用这个实例不需要再经历一次隐式实例化生成实例。如此显式实例化的好处是,编译器不需要在每次需要用的模板的时候重新生成函数,提高效率。
至此,显式实例化和显式具体化区别已经明了:一个是为了提高效率,一个是为了适应更多的类型。隐式实例化、显式实例化和显式具体化统称具体化(specialization)。
void may(int); // #1float may(float, float = 3); // #2void may(char); // #3char may(const char &); // #4template<class T> void may(const T &); // #5与may('B');进行匹配,如此,便有优先顺序问题。一般的优先顺序如下(高至底):
①完全匹配,但常规函数优于模板;
②提升转换(char和short转为int,float转为double);
③标准转换(int转为char,long转为double);
④用户自定义转换。
如此,#1优先于#2(因为②);#3#4#5优于#1#2(因为①);#3#4优于#5(因为#5是模板,#3#4是常规函数),剩下的是在#3#4中选。
完成匹配之前,需要说下完全匹配:指类型对口(int对int,char对char等),就是说不需要转换。但是,某些种类的转换对完全匹配无影响,如表:
但是,对于非const的数据的指针、引用将优先与非const指针和引用参数匹配。
综合上面原则可知,#3#4产生了二意,不能匹配。
接下来讲考虑无完全匹配的情况,或者在多个完全匹配函数再匹配的情况。这是后遵循的是“最具体”原则,意思是:转换最少的那种为优。如
template <class Type> void recycle (Type t); // #1
template <class Type> void recycle (Type * t); // #2
与recycle(&i)(i是整型)匹配。如果与#1进行匹配,Type将视作int*;与#2匹配Type将视作int。显然是选择#2,因为前者(int转int*)比后者(int直接匹配int)转换少。
如果上述原则都不能正确匹配,会产生“二意性”错误。
……话说回来,不需太深入了解,如果产生二意性,编译器自然会提醒的。
- 第九天(函数进阶 · 二)
- 第九天H5进阶
- 宅急送 项目第九天 JBPM 进阶
- 第九天 框架之痛-Spring MVC(二)
- 安卓学习第九天:面向对象基础(二)
- 第八天(函数进阶 · 一)
- 第一阶段第九天(函数递归调用,一维数组)
- 第九天(数组,预定义数组,函数,使用数据库)
- 分析函数进阶(二)
- 第九天 多线程 (续)
- Android 第九天(上午)
- Android 第九天(下午)
- Android 第九天(晚上)
- 第九天(5道)
- 第九天-复习(第一阶段)
- Java进阶学习第九天——Servlet入门
- 第九天
- 第九天
- Data Structures: Source Listings
- Floyd-Warshall算法详解
- Java Threads 多线程10分钟参考手册
- The Art of Unix Programming
- 群啊群
- 第九天(函数进阶 · 二)
- FW - Semi-Aggregate Property (need to H)
- android GPS LocationListener类
- Ubuntu文件系统和目录结构
- 网宿校招笔试题节选
- hdu1711
- DirectShow开发自己的Filter
- 解决Tomcat乱码
- iOS-正则表达式匹配数字或网址