C++--第20课 - 函数模板

第20课 - 函数模板

1. C++中如何交换两个变量的值?

void Swap(int& a, int& b)

{

int t = a;

a = b;

b = t;

}

void Swap(float& a, float& b)

{

float t = a;

a = b;

b = t;

}

void Swap(char *& a, char *& b)

{

char *& t = a;

a = b;

b = t;

}

除了类型不同,函数体代码完全相同。C++强调代码复用,那如何来解决代码冗余的问题呢?

2. 泛型编程

不考虑具体类型的编程模式。我们先考虑算法,再考虑类型。

对于Swap函数可以考虑下面的泛型写法

void Swap(T& a, T& b)

{

T t = a;

a = b;

b = t;

}

Swap泛型写法中的T不是一个具体的数据类型,而是泛指任意的数据类型。

(1)函数模板

提供了一种特殊的函数可用不同类型进行调用,看起来和普通的函数很相似,区别是类型可被参数化。

template <typename T>

void Swap(T& a, T& b)

{

T t = a;

a = b;

b = t;}

(2)函数模板的语法规则

template关键字用于声明开始进行泛型编程。

typename关键字用于声明泛指类型。

(3)函数模板的应用

自动类型推导调用

具体类型显示调用

#include <cstdlib>

#include <iostream>

using namespace std;

template<typename T>

void Swap(T& a, T& b)

{

    T t = a;

    a = b;

    b = t;

}

int main(int argc, char *argv[])

{

    int a = 1;

    int b = 2;

    Swap(a, b); //自动类型推导:a和b均是int,因此类型参数T为int

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

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

    float fa = 3;

    float fb = 4; 

    Swap<float>(fa, fb);  //显示类型调用:用loat替换参数类型T 

    cout<<"fa = "<<fa<<endl;

    cout<<"fb = "<<fb<<endl;

    char ca = 'a';

    char cb = 'b';

    Swap(ca, cb);

    cout<<"ca = "<<ca<<endl;

    cout<<"cb = "<<cb<<endl;

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

a = 2

b = 1

fa = 4

fb = 3

ca = b

cb = a

3. 泛型编程初体验(排序算法)

#include <cstdlib>

#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 SelectSort(T array[], int length)//选择排序

{

         for(int i = 0; i<length; i++) //全循环

         {

                  T min = array[i];

                  int index = i;

                  for(int j = i+1; j < length; j++)

                  {

                           if( array[j] < min)  //找最小

                           {

                                    min = array[j];

                                    index = j; 

                           }       

                  }

                  Swap(array[i],array[index]);             

         }

}

int main(int argc, char *argv[])

{

    int array[] = {3, 2, 5, 3 , 4};

    SelectSort<int>(array, 5);

    for(int i=0; i<5; i++)

    {

        cout<<array[i]<<endl;

    }

    char ca[] = {'b', 'c', 'a', 'e', 'd', 'f'};

    SelectSort(ca, 6);

    for(int i=0; i<6; i++)

    {

        cout<<ca[i]<<endl;

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

2

3

3

4

5

a

b

c

d

e

f

l  函数模板的深入理解:

编译器并不是把函数模板处理成为能够处理任意类型的函数。

编译器从函数模板通过具体类型产生不同的函数。(用几个类型产生几个函数)

编译器会对函数模板进行两次编译:在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

问:

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

4. 函数模板与函数重载

函数模板可以像普通函数一样被重载。C++编译器优先考虑普通函数,如果函数模板可以产生一个更好的匹配,那么选择模板。可以通过空模板实参列表的语法限定编译器只通过模板匹配。

#include <cstdlib>

#include <iostream>

using namespace std;

int Max(int a, int b) //与函数模板重名,这两者重载。 这两个函数不会发生冲突。

{

    cout<<"int Max(int a, int 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;

}

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 argc, char *argv[])

{

    int a = 1;

    int b = 2;

    cout<<Max(a, b)<<endl;

    cout<<Max<>(a, b)<<endl;

    cout<<Max(3.0, 4.0)<<endl;

    cout<<Max(5.0, 6.0, 7.0)<<endl;

    cout<<Max('a', 100)<<endl; //不允许自动类型转换,只能调用普通函数。

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

int Max(int a, int b)

2

T Max(int a, int b)

2

T Max(int a, int b)

4

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

T Max(int a, int b)

T Max(int a, int b)

7

int Max(int a, int b)

100

注意事项:

函数模板不允许自动类型转化,普通函数能够进行自动类型转换。

cout<<Max(‘a’, 100)<<endl; 必然只会调用普通函数。

5. 多参数函数模板

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

template<typename T1, typename T2, typename RT>

RT Add(T1 a, T2 b)

{

return static_cast<RT>(a + b)

}

cout<<Add<char, float, double >(‘a’, 100)<<endl;

问题:

多类型参数的模板可以进行自动类型推导吗?

当生命的类型参数为返回值类型时,无法进行自动类型推导。

#include <cstdlib>

#include <iostream>

using namespace std;

template<typename RT, typename T1, typename T2>

RT Add(T1 a, T2 b)

{

    return static_cast<RT>(a + b);

}

int main(int argc, char *argv[])

{

    cout<<Add<double, char, float>('a', 100.0f)<<endl;

    cout<<Add<double>('a', 100.0f)<<endl;   //只定义返回值类型

    //cout<<Add('a', 100.0f)<<endl;  这样写会报错

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}运行结果:

197

197

l  不完美的解决方案

将返回值类型参数声明到第一个参数的位置,调用时只需显示声明反水类型参数即可。

小结:

函数模板起始是一个具有相同行为的函数家族。

函数模板可以根据类型实参对函数进行推导调用。

函数模板可以显示指定的类型参数。

函数模板可以被重载。

猜你喜欢

转载自www.cnblogs.com/free-1122/p/11336241.html