C++语言- 泛型编程之函数模板(详解)
小标 2018-07-25 来源 : 阅读 1182 评论 0

摘要:本文主要向大家介绍了C++语言- 泛型编程之函数模板(详解),通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

本文主要向大家介绍了C++语言- 泛型编程之函数模板(详解),通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

当我们想写个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;

}

 

运行打印:

0x41ba980x41ba70

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

 

多参数函数模板

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

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;

}

 

运行打印:

22.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

本文由职坐标整理并发布,了解更多内容,请关注职坐标编程语言C/C+频道!


本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程