呼,,,终于到了最紧要的关头了。
1、函数签名(函数声明):它包括了函数名和函数的参数类型。一个函数只对对应一个唯一的签名(不唯一,就会出错)。
因此,两个同名函数但参数类型不同,就是两个不同的函数签名,程序就是这样去识别、分辨调用的是哪个函数。
由此,我们产生了我们以为相同的情况:函数名相同,但参数类型或参数的个数不同的。尽管还是函数签名不同,但我们把这种情况称为“函数重载”。
注意:1、函数签名不包括返回值,因此它不会管返回值是什么东西。
2、尽管调用时函数没有这样的类型,但可以隐式转换的,也认可是调用该函数。否则出错。
例:重载: long larger(long a,long b);
double larger(double a,double b);
调用时是: c=larger(float_value1,double_value2);
因为float_value是可以隐匿转换成double的,所以调用的是第二个函数。也是函数重载的情况。
2、当重载是指针时,应小心:
sum(int* p)和sum(int a[ ]) 会认为是一个函数签名,因为都是说的int*
当是引用时,也要小心:
int sum(int a);
int& sum(int& a);
对于重载来说是没有区别的(返回值对于int加不加&,引用,是没有区别的,但对于类的时候有区别)
当然对于机制来说是有区别的,一个是传递副本,一个不传递副本(引用)。
如果你坚持要引用是子函数内部的引用的话,就必须在前面加上const,因为这样表示是数据,而不是变量名(因为函数完了,变量会消亡)
同样下面函数签名是一样的:
long& sum(int a);
long& sum(const int a);
这是因为是值传递,副本对拷,在函数中永远不会修改它的初始值,所以认识认为是一个函数。
long& sun(int* a);
long& sun(const int* a);
这里虽然是副本传递,但指针可以在函数中解析到初始值,这个const就规定了不能修改主函数中的初始值,因此这是两个不同的函数签名。
但这样又是一样的:
long& sum(int* const a);
long& sum(int* a); //因为副本传递时,指针本身就是原样复制,指针本身是没有改变原指针的值。
下面不是一样的,虽然都是指针:
void show(const char* a);
void show(const string& p);
3、返回值与重载无关(重载只与签名有关)。这里要说说返回值是引用的情况:
当子函数返回值是引用时,那么,它是返回的一个别名,因此可以做为左值(lvalue)。当然也可作右值(rvalue)
long& sum(int& a,int &b)
{ return a;}
所以在主函数中可以这样写 sum(c,d)=3, 因为返回是第一个参数的引用,故左边函数调用后相当于是C,表达式变为c=3, 因此是正确的。
4、模板来了:Template <Class T>T sum(T a,T b)
原因,因为重载要写多个子函数体,很烦,于是就产生模板。由它来产生类似的子函数,每产生的一个子函数都是模板的一个实例或称“模板实例化”。
Template: 关键字,表明后面是模板
< >: 表示模板中要用的参数
Class : 关键字,表明后面参数是一个类型(类或基本类型、或自定类型),也可以用TypeName是一个意思
T :参数,取自Type,实际上你想取什么就取什么,如My_Type。起替代的作用。
后面的就类似函数声明,当用int时,整个函数就造就一个新实例:int sum(int a,int b)
5、实例的产生:
当调用函数时,会“首先”检查有没有一样的函数,如果没有,“然后”再从模板中生成一个实例。注意,这个实例只产生一次,下次再调用相同类型的,就直接用这个类型,而不是用一次模板就产生一个实例。
另一个就是:模板只在编译和链接中产生 ,一旦生成exe文件,就没有模板的概念了。而且,一个项目几个文件中只要产生一次实例,所有都使用这一个实例,而不是谁调用一次就产生一个实例。
#include <iostream>
using namespace std;
template <class T>T sum(T a,T b)
{
return a+b;
}
double sum(double m,double n)
{
return 5.0;
}
int main(int argc, char *argv[])
{
int a=1,b=3;
cout<<sum(a,b)<<endl;
double m=3.0,n=4.0;
cout<<sum(m,n)<<endl;//先查找,因为有,所以不在模板中生成
return 0;
}
6、模板总不能覆盖用户要求,比如:
一、模板不能判断产生哪种,具有二义性时,他会旗帜鲜明地罢工,此时需耐心给它指明道路;
二、模板虽然能够正常工作,但我想玩弄它,强制指定它用某种情况。
funName<T>(arglist) // 调用的函数名后用尖括号强制使用某类型
如templat<class T> T sum(T a,T b)
如果调用时用了: sum(int a, double),模板没法选择用int或double,因此你得指明道路 sum<double>(a,b)
还有就是正常情况:sum(int a,int b) 但我就是想让它用long,于是可以 sum<long>(a,b)
强制过程,可以扩大或缩小范围,都是可行了。
7、说到底,模板就和函数一样:
一、可以发生重载,比如,模板的特殊化、另一个具体的函数;
二、模板还可以嵌套,即模板中再套用一个模板。如函数中套用函数一样。
8、再进一步,带有多个参数的模板。
一、两个参数的:比如我们来一个带返回值的参数。不是说返回两个值,而是规定返回类型必须是规定的参数类型。
template <class T_return,class T_inside>T_return sum(T_inside a,T_indside b)
主函数中调用:cout<<sum<int>(1.5,3.6); //根据模板形参用double,本来应返回5.1,但这里规定回返是整形,故返回5
cout<<sum<double,double>(5,7); // 强制规定用double,故返回值是12.0
二、带多个参数。
判断是否在作用域:template<class T,int low,int upper>bool isin_range(T a)
{ return a<low && a>upper; } //通过上下限,来比较判断是否在其中。
主函数中调用: cout<<isin_range<double,0,5000>(a); // 这里的上下限,最好用一个函数来,这样才能动态更好控制,用数字也无错。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
前面模板中的T大多基本类型的,但还有三种类型作参数T也可用T:
一、整形类型,比如上面带多参数时,用的参数double,当然还有int,long只要是整形就可以;
二、枚举类型。emun a={.......} 回头再复习 一下
三、指针或引用。比如对象、函数、类成员的指针或引用作为参数T。
===================================================================================================================
激动人心的时刻,函数的指针即将到来
===================================================================================================================
===================================================================================================================
9、函数指针
函数指针:同其它基础类型的指针一样,函数也有指针,它指向“函数的地址”。通过函数指针可以调用对应的函数。
函数指针虽然只是函数的地址,但“隐含”了这个函数的返回值类型、参数类型等,只有相同的类型函数,其指针才能相互赋值。就如同int指针可以接收
另一int指针的值一样。它与”函数名称“没有关系。
10、函数指针的初始化和赋值
函数指针形式: int (*pfun)(int ,int ) //里面类型可换。与模板相反,这它是名字可换,但类型一必须一致。
初始化时,只须将函数名给赋值给它,或者直接用0置空。这样这个指针就相当于”函数名“类似于”数组名“一样,用它带参数就可以调用对应的函数了。
int sum(int a,int b);
主函数中: int (*pfun)(int ,int )=0; //初始化为0,不指向任何函数,也可 int (*pfun)(int,int)=sum;直接赋值sum ,即将sum函数指针给pfun
pfun=sum; //赋值
cout<<pfun(3,5); //用函数指针,类型用数组名一样,调用sum函数。
11、回调函数:把函数的地址(指针)作为参数,传递给另外一个函数,那这个被传递的指针所指的函数就是回调函数。
常见的有callback
12、函数指针数组:
由函数指针组成的数组,这样就可以由数组名加索引再加上参数来调用对就在的函数。
注意:由于指针类型的特点,这样的元素它们对应的函数的返回值类型、参数类型是一样的
double sum(double a,double b);
double product(double a ,double b)
double cube(double a,double b)
因此定义: double (*pfun[3])(double,double)={sum,product,cube};
调用第二个元素对应的函数: cout<<pfun[1](3.2,6.3);