(九)函数的“化身”百态:重载、模板及函数指针

呼,,,终于到了最紧要的关头了。

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





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值