C++泛型编程之函数模板

1.基本概念

模板是泛型编程的基础,包括函数模板和类模板两类
其作用是建立一个通用函数,该函数的返回值和形参类型不具体而用一个虚拟类型代表,达到简化的目的
语法templateT:为通用数据类型

如要实现两数交换的函数 int类型的写法如:

void swapInt(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

而如果要交换doubule 就不适用,需更改为:

void swapDouble(double &a, double &b)
{
    double temp = a;
    a = b;
    b = temp;    
}

这种情况下,可通过使用泛型编程实现任意数据类型的交换

//声明
template<typename T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;       
}

该模板的使用方法有两种
a.自动类型推导 事先不声明虚拟类型T,而由编译器自动判断

//1.自动类型推导
void test01()
{
    int a = 10;
    int b = 20;
    mySwap(a,b);
    //mySwap(a,b);
    cout << "a = "<< a << endl;
    cout << "b = "<< b << endl;
}

b.事先声明T的真实类型

mySwap<int>(a,b);

注意事项:

使用模板必须确定出通用数据类型T,并且能够推导出一致类型。即使函数中无需传参,也要声明T的类型(自己随便指定一个 如int)

基于上述知识,设计一个排序函数,实现对不同数据类型数组进行排序的功能

排序算法为选择排序,降序
测试为 char数组、int数组
Demo:

#include 
using namespace std;
// 利用函数模板封装一个排序函数,实现对不同数据类型数组进行排序的功能
// 排序算法为选择排序,降序
// 测试为 char数组、int数组
template<typename T>
void Sort(T *a,int Size)
{
    for(int i = 0;i < Size; i++)
    {
        int max = i;
        for(int j = i + 1; j < Size; j++)
        {
            if (a[j] > a[max])
            {
                max = j;
            }
        }
        if(max != i)
        {
            //交换max
            T temp = a[i];
            a[i] = a[max];
            a[max] = temp;
        }
    }
    for(int i = 0; i < 5; i++)
    {
        cout<<a[i]<<endl;
    }
}
int main()
{
    int  a[6] = {1,2,3,4,5,6};
    Sort(a,6);
    return 0;
}

2.普通函数与函数模板

(1)普通函数与函数模板二者区别

普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换

Demo:

#include 
using namespace std;
//1,普通函数调用可以发生隐式类型转换
int myAdd01(int a,int b)
{
    return a+b;
}
//2. 函数模板自动类型推导时不可发生隐式类型转换
//3. 函数模板显式指定类型时可发生隐式类型转换
template <typename T>
T myAdd02(T a, T b)
{
    return a+b;
}

void test01()
{
    int a = 10;
    int b = 20;
    char c = 'c';
    cout << myAdd02<int>(a, c) << endl;
}
int main()
{
    test01();
    return 0;
}

如何理解呢?我认为普通函数和函数模板显式指定参数类型这两种逻辑上是一致的,即都有一个类似强制转换的过程。而函数模板自动类型转换中,根据上文的注意事项中,要确保T是一致的,所以当参数为两个不同类型的数据时,自然会报错。建议使用显式类型转换。

(2)普通函数与函数模板的调用规则

1.如果函数模板和普通函数均可实现,则优先调用普通函数
2.可以通过空模板参数列表强制调用函数模板
3.函数模板也可重载
4,如果函数模板可以产生更好匹配,优先调用函数模板

Demo:

#include 
using namespace std;
void myPrint(int a, int b)
{
    cout << "普通函数"<<endl;
}
template <typename T>
void myPrint(T a, T b)
{
    cout<<"函数模板"<<endl;
}
template <typename T>
void myPrint(T a, T b,T c)
{
    cout<<"函数重载模板"<<endl;
}
void test01()
{
    int a = 10;
    int b = 20;
    myPrint(a,b); //调用普通函数
    myPrint<>(a,b);//调用函数模板 通过空参数列表实现强制调用模板
    myPrint(a,b,10);//调用重载函数模板
    char c1 = 'a';
    char c2 = 'c';
    myPrint(c1,c2);//调用产生更好的匹配的函数
}
int main()
{
    test01();
    return 0;
}

总结:这4种原则可理解为:编译器优先调用可产生更好匹配的函数

3.模板局限性

模板并不是万能的,有些特定数据类型如自定义类型,需要使用具体化方式做特殊实现

#include 
#include 
using namespace std;
class Person
{
private:
    /* data */
public:
    Person(string name, int age);
    string m_name;
    int m_age;
    ~Person();
};
Person::Person(string name, int age)
{
    m_name = name;
    m_age = age;
}
Person::~Person()
{
}
//对比两个数据是否相等
template<typename T>
bool myCompare(T &a, T &b)
{
    if(a == b)
    {
        return true;
    }
    else
    {
        return false;
    }  
}
//利用具体化Person的版本实现代码,具体化优先调用
template<> bool myCompare(Person &a, Person &b)
{
    if(a.m_age == b.m_age && a.m_name == b.m_name)
    {
        return true;
    }
    else
    {
        return false;
    }  
}
void test01()
{
    int a = 10;
    int b = 20;
    bool ret = myCompare(a,b);
    cout << ret <<endl;
}
void test02()
{
    Person p1("Tom", 10);
    Person p2("Tom", 10);
    bool ret = myCompare(p1,p2);
    cout << ret <<endl;
}
int main()
{
    test02();
    return 0;
}

你可能感兴趣的:(Cpp,c++)