zoukankan      html  css  js  c++  java
  • 25.C++- 泛型编程之函数模板(详解)

    本章学习:

    1)初探函数模板

    2)深入理解函数模板

    3)多参函数模板

    4)重载函数模板


    当我们想写个Swap()交换函数时,通常这样写:

    void Swap(int& a, int& b)
    {
        int c = a;
        a = b;
        b = c;
    }

    但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量。更糟的是,还会增加可执行文件的大小.

     

    所以C++引入了泛型编程概念

    在C++里,通过函数模板类模板来实现泛型编程(类模板在下章将讲解)

    函数模板

    • 一种特殊的函数,可通过不同类型进行调用
    • 函数模板是C++中重要的代码复用方式
    • 通过template关键字来声明使用模板
    • 通过typename关键字来定义模板类型

    比如:  

    template <typename T>       //声明使用模板,并定义T是一个模板类型
    
    void Swap(T& a, T& b)           //紧接着使用T
    {
        T c = a;
        a = b;
        b = c;
    } 

    当我们使用int类型参数来调用上面的Swap()时,则T就会自动转换为int类型.

    函数模板的使用

    • 分为自动调用显示调用

    例如,我们写了一个Swap函数模板,然后在main()函数里写入:

    int a=0;
    int b=1;
    
    Swap(a,b);                   //自动调用,编译器根据a和b的类型来推导
    
    float c=0;
    float d=1;
    
    Swap<float>(c,d);           //显示调用,告诉编译器,调用的参数是float类型

    初探函数模板

    写两个函数模板,一个用来排序数组,一个用来打印数组,代码如下:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template < typename T >
    void Sort(T a[], int len)
    {
           for(int i=1;i<len;i++)
           for(int j=0;j<i;j++)
           if(a[i]<a[j])
           {
                   T t=a[i];
                   a[i]=a[j];
                   a[j]=t;
           }
    }
    
    template < typename T >
    void Println(T a[], int len)
    {
         for(int i=0; i<len; i++)
        {
            cout << a[i] << ", ";
        }
        cout << endl;
    }
    
    int main()
    {
        int a[5] = {5, 3, 2, 4, 1};
    
        Sort(a, 5);            //自动调用,编译器根据a和5的类型来推导
        Println<int>(a, 5);    //显示调用,告诉编译器,调用的参数是int类型
    
        string s[5] = {"Java", "C++", "Pascal", "Ruby", "Basic"};
    
        Sort(s, 5);
        Println(s, 5); 
    return 0; }

    运行打印:

    1,2,3,4,5,
    Basic,C++, Java,Pascal,Ruby,

    深入理解函数模板

    为什么函数模板能够执行不同的类型参数?

    答:

    • 其实编译器对函数模板进行了两次编译
    • 第一次编译时,首先去检查函数模板本身有没有语法错误
    • 第二次编译时,会去找调用函数模板的代码,然后通过代码的真正参数,来生成真正的函数
    • 所以函数模板,其实只是一个模具,当我们调用它时,编译器就会给我们生成真正的函数.

     

    试验函数模板是否生成真正的函数

    通过两个不同类型的函数指针指向函数模板,然后打印指针地址是否一致,代码如下:

    #include <iostream>
    
    using namespace std;
    template
    <typename T> void Swap(T& a, T& b) { T c = a; a = b; b = c; } int main() { void (*FPii)(int&,int&); FPii = Swap ; //函数指针FPii void (*FPff)(float&,float&); FPff = Swap ; //函数指针FPff cout<<reinterpret_cast<void *>(FPii)<<endl; cout<<reinterpret_cast<void *>(FPff)<<endl; //cout<<reinterpret_cast<void *>(Swap)<<endl; //编译该行会出错,因为Swap()只是个模板,并不是一个真正函数 return 0; }

    运行打印:

    0x41ba98
    0x41ba70

    可以发现两个不同类型的函数指针,指向同一个函数模板,打印的地址却都不一样,显然编译器默默帮我们生成了两个不同的真正函数

     

    多参数函数模板

    在我们之前小节学的函数模板都是单参数的, 其实函数模板可以定义任意多个不同的类型参数,例如:

    template <typename T1,typename T2,typename T3>       
    T1 Add(T2 a,T3 b)
    {
        return static_cast<T1>(a+b);      
    }

    注意:

    • 工程中一般都将返回值参数作为第一个模板类型
    • 如果返回值参数作为了模板类型,则必须需要指定返回值模板类型.因为编译器无法推导出返回值类型
    • 可以从左向右部分指定类型参数 

     

    接下来开始试验多参数函数模板

    #include <iostream>
    
    using namespace std;
    
    template<typename T1,typename T2,typename T3>       
    T1 Add(T2 a,T3 b)
    {
           return static_cast<T1>(a+b);      
    }
    
    int main()
    {
           // int a = add(1,1.5);       //该行编译出错,没有指定返回值类型
    
           int a = Add<int>(1,1.5);
           cout<<a<<endl;                  //2
    
           float b = Add<float,int,float>(1,1.5);
           cout<<b<<endl;                  //2.5
    
           return 0;
    }

    运行打印:

    2
    2.5

     

    重载函数模板

    • 函数模板可以像普通函数一样被重载
    • 函数模板不接受隐式转换
    • 当有函数模板,以及普通重载函数时,编译器会优先考虑普通函数
    • 如果普通函数的参数无法匹配,编译器会尝试进行隐式转换,若转换成功,便调用普通函数
    • 若转换失败,编译器便调用函数模板
    • 可以通过空模板实参列表来限定编译器只匹配函数模板

     

    接下来开始试验重载函数模板

    #include <iostream>
      
    using namespace std; 
    
    template <typename T>        
    T Max(T a,T b)
    {
        cout<<"T Max(T a,T b)"<<endl;   
        return a > b ? a : b;
    } 
    
    template <typename T>        
    T Max(T* a,T* b)                    //重载函数模板 
    {
        cout<<"T Max(T* a,T* b)"<<endl;    
        return *a > *b ? *a : *b;
    } 
    
    
    int Max(int a,int b)                //重载普通函数 
    {
        cout<<"int Max(int a,int b)"<<endl;   
        return a > b ? a : b;
    }  
     
    int main()
    {  
        int a=0;
        int b=1;
        
        cout<<"a:b="<<Max(a,b) <<endl ;        //调用普通函数 Max(int,int)
        
        cout<<"a:b="<<Max<>(a,b)<<endl;        //通过模板参数表 调用 函数模板 Max(int,int)
        
        cout<<"1.5:2.0="<<Max(1.5,2.0)<<endl;   
         //由于两个参数默认都是double,所以无法隐式转换,则调用函数模板 Max(double,double)
        
        int *p1 = new int(1);
        int *p2 = new int(2); 
     
        cout<<"*p1:*p2="<<Max(p1,p2)<<endl;  // 调用重载函数模板 Max(int* ,int* )    
        
        cout<<"'a',100="<< Max('a',100)<<endl;        
        //将char类型进行隐式转换,从而调用普通函数 Max(int,int)
        
        
        delete p1;
        delete p2; 
        
        return 0;
    }

    运行打印:

    int Max(int a,int b)
    a:b=1
    T Max(T a,T b) a:b=1
    T Max(T a,T b) 1.5:2.0=2
    T Max(T* a,T* b) *p1:*p2=2
    int Max(int a,int b) 'a',100=100

      接下来下章来学习: 26.C++- 泛型编程之类模板(详解)

  • 相关阅读:
    20169218 2016-2017-2 《网络攻防实践》第八周学习总结
    20169218 2016-2017-2 《网络攻防实践》第七周学习总结
    20169218 2016-2017-2 《网络攻防实践》第六周学习总结
    20169218 2016-2017-2 《网络攻防实践》第五周学习总结
    20169218 2016-2017-2 《网络攻防实践》第四周学习总结
    十三周作业—使用Metaspoit攻击MS08-067
    20169206 2016-2017-2 《网络攻防实践》课程总结
    《网络攻防》第十四周作业-免杀
    《网络攻防》 第十二周作业 SQL注入
    《网络攻防》 第十一周作业
  • 原文地址:https://www.cnblogs.com/lifexy/p/8761325.html
Copyright © 2011-2022 走看看