资深流水灯工程师

资深流水灯工程师

支持泛型变成的语言最适合数据结构的学习:比如C++,C++的模板技术。

C++交换变量的方法

1、宏代码块

程序实例1:宏定义实现数据的交换

#include <iostream>

using namespace std;


#define Swap(t, a, b)   \
do                      \
{                       \
    t c = a;            \
    a = b;              \
    b = c;              \
}while(0)



int main()
{

    int a = 1;
    int b = 2;

    Swap(int, a, b);

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;


    double m = 3;
    double n = 4;
    Swap(int, m, n);


    cout << "m = " << m << endl;
    cout << "n = " << n << endl;


    return 0;
}

2、函数,定义多个重载的函数来进行不同类型的交换

程序实例2:函数重载实现数据的交换 

#include <iostream>

using namespace std;


//函数重载
void Swap(int& a, int& b)
{
    int t = a;
    a = b;
    b = t;
}

//函数重载
void Swap(double& a, double& b)
{
    double t = a;
    a = b;
    b = t;
}



int main()
{

    int a = 1;
    int b = 2;

    Swap(a, b);

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;


    double m = 3;
    double n = 4;
    Swap(m, n);


    cout << "m = " << m << endl;
    cout << "n = " << n << endl;


    return 0;
}

不管是宏定义,还是重载函数,进行数据交换都没那么方便,宏不安全,函数重载太麻烦,用泛型编程就不需要这么麻烦。

泛型编程的概念

不考虑具体数据类型的编程方式。

对于Swap函数可以使用泛型写法,T不是一个具体的数据类型,而是泛指任意的数据类型。

void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}

C++的函数模板(template)

一种特殊的函数,可以用不同类型进行调用,看起来跟普通函数很相似,区别在于类型可以被参数化

template <typename T>

void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}

template关键字是告诉编译器要开始泛型编程;

<typename T>告诉编译器 T 是一个泛指类型。

函数模板的使用

1、让编译器自动推导类型并调用;

2、具体类型显式调用

程序实例3:泛型编程实现数据的交换

#include <iostream>

using namespace std;


template <typename T> //告诉编译器要进行泛型编程
void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}


int main()
{
    int a = 0;
    int b = 1;


    Swap(a, b); //自动推导 T为int

    cout <<"a = " << a << endl;
    cout <<"b = " << b << endl;

    float c = 2.5;
    float d = 3.14;

    Swap<float>(c, d); //显式调用,T为float

    cout <<"c = " << c << endl;
    cout <<"d = " << d << endl;

    return 0;
}

程序实例4:泛型编程实现数据的排序

#include <iostream>

using namespace std;

//泛型编程:交换函数
template <typename T> //告诉编译器要进行泛型编程
void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}

//泛型编程:排序函数
template <typename T>
void Sort(T a[], int len)
{
    for(int i=0; i<len; i++)
    {
        for(int j=i; j<len; j++)
        {
            if(a[i]>a[j])
            {
                Swap(a[i], a[j]);
            }
        }
    }
}

//泛型编程:打印函数
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, 1, 2, 4};

    Sort(a, 5);

    println(a,5);

    string s[5] = {"Java", "C++", "C", "Python","C#"};

    Sort(s, 5);

    println(s,5);

    return 0;
}

函数模板到底是什么

编译器从函数模板通过具体类型产生不同的函数;

编译器会对函数进行两次编译,第一次对模板代码进行编译,第二次对参数替换后的代码进行编译。

函数模板本身不是函数,它只是一个生产函数的模具,因此函数模板本身不允许隐式的类型转换,自动推导类型时,必须严格匹配;

程序实例5:

#include <iostream>

using namespace std;

template <typename T>
void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}

typedef void(FuncI)(int&, int&); //定义函数类型FuncI
typedef void(FuncD)(double&, double&); //定义函数类型FuncD

int main()
{
    FuncI* pi = Swap; //编译器自动推导T为int
    FuncD* pd = Swap; //编译器自动推导T为double

    cout <<"pi = " << reinterpret_cast<void*>(pi) <<endl;
    cout <<"pd = " << reinterpret_cast<void*>(pd) <<endl;


    return 0;
}

输出结果:

pi = 0x402d90
pd = 0x402d40

函数模板可以定义任意多个不同的参数类型

语法:

template <typename T1, typename T2, typename T3>

对于多参数函数模板,无法自动推导出返回值的类型,可以从左向右部分指定类型参数,工程中一般将返回值的参数类型作为第一个参数类型。

//T1 = int , T2 = double, T3 = double
int r1 = Add<int>(0.5, 0.8); 

//T1 = int , T2 = float, T3 = double
int r2 = Add<int, float>(0.5,0.8);

//T1 = int , T2 = float, T3 = float
int r3 = Add<int, float, float>(0.5,0.8);

程序实例6:多参数类型函数模板

#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 r1 = Add<int>(0.5, 0.8);

    double r2 = Add<double, float>(0.5,0.8);

    float r3 = Add<float, float, float>(0.5,0.8);

    cout << "r1 = " << r1 << endl;
    cout << "r2 = " << r2 << endl;
    cout << "r3 = " << r3 << endl;

    return 0;
}

输出结果:

r1 = 1
r2 = 1.3
r3 = 1.3

函数重载遇到函数模板相遇会发生什么

函数模板可以像普通函数一样被重载:

C++编译器有限考虑普通函数,如果函数模板可以产生一个更好的匹配,就选择模板;可以通过空模板实参列表限定编译器只匹配模板

程序实例7:函数重载与函数模板

#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;
}

int max(int a, int b)
{
    cout << "T Max(T a, T b)" << endl;
    return a>b?a:b;
}


template <typename T>
T Max(T a, T b, T c)
{
    cout << "T Max(T a, T b, T c)" << endl;
    return max(max(a,b), c);
}

int main()
{
    int a = 1;
    int b = 2;

    cout << endl << Max(a, b) << endl; //优先考虑普通函数,使用int max(int a, int b)

    cout << endl << Max(3.0, 4.0) << endl; //普通函数不能匹配,使用T Max(T a, T b)

    cout << endl << Max(5.0, 6.0, 7.0) << endl;//普通函数不能匹配,使用T Max(T a, T b, T c)


    return 0;
}

输出结果:

T Max(T a, T b)
2

T Max(T a, T b)
4

T Max(T a, T b, T c)
7

C++的类模板

以相同的方式处理不同的类型

在类声明前使用template进行标识

 <typename T>用于说明类中使用了泛指类型T

类模板的使用

只能显式指出具体类型,无法自动推导,使用具体类型<Type>定义对象

#include <iostream>

using namespace std;


template <typename T>
class Op
{
public:
    T process(T v)
    {
        return v*v;
    }
};

int main()
{
    Op<int> op1; //显式定义对象
    Op<double> op2; //显式定义对象

    cout << "5*5= " << op1.process(5) << endl;
    cout << "0.3*0.3= " << op2.process(0.3) << endl;


    return 0;
}
10-07 18:52