C++提高编程---模板---函数模板

发布时间:2024年01月21日

目录

一、模板

二、函数模板

1. 函数模板

2.函数模板注意事项

3.函数模板案例

三、普通函数和模板函数的区别

四、普通函数和模板函数的调用规则

五、模板的局限性


一、模板

模板的实用性很强,但不能完全完全复制使用

c++提供另一种编程思想? 泛型编程? 主要利用模板

模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

模板是一种对类型进行参数化的工具;

通常有两种形式:函数模板和类模板;

函数模板针对仅参数类型不同的函数;

类模板针对仅数据成员和成员函数类型不同的类。

使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。

注意模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

二、函数模板

1. 函数模板

语法:

template<typename T>? // typename 也可以是? class

函数声明或定义

// 注释

template?? --- 声明创建模板

typename?? --- 表明其后面的符号是一种数据类型,可以用class代替

T????????? --- 通用数据类型,名称可以替换,通常为大写字母

示例:

#include<iostream>

using namespace std;

?

// 函数模板

?

// 两个整形交换函数

void swp_int(int &a,int &b)

{

    int temp = a;

    a=b;

    b=temp;

}

?

// 交换两个浮点型的函数

void swp_double(double &a,double &b)

{

    double temp =a;

    a=b;

    b=temp;

}

?

// 函数模板

template<typename T>? // 声明一个模板,告诉编译器中紧跟着T不要报错

void my_Swap(T &a,T &b)

{

    T temp =a;

    a=b;

    b=temp;

}

?

void test01()

{

    int a=10;

    int b=20;

    cout<<"交换前 a = "<<a<<" b = "<<b<<endl;

    swp_int(a,b);

    cout<<"交换后 a = "<<a<<" b = "<<b<<endl;

?

    // 利用函数模板

    // 两种方法使用函数模板

    // 1. 自动类型推导

    cout<<"自动--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;

    my_Swap(a,b);

    cout<<"自动--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;

?

    // 2. 显示指定类型

    cout<<"显示--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;

    my_Swap<int>(a,b);

    cout<<"显示--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;

?

?

    double c =12.3;

    double d = 23.4;

    cout<<"交换前 c = "<<c<<" d = "<<d<<endl;

    swp_double(c,d);

    cout<<"交换后 c = "<<c<<" d = "<<d<<endl;

?

    // 1. 自动类型推导

    cout<<"自动--模板函数--交换前 c = "<<c<<" d = "<<d<<endl;

    my_Swap(c,d);

    cout<<"自动--模板函数--交换后 c = "<<c<<" d = "<<d<<endl;

?

    // 2. 显示指定类型

    cout<<"显示--模板函数--交换前 c = "<<c<<" d = "<<d<<endl;

    my_Swap<double>(c,d);

    cout<<"显示--模板函数--交换后 c = "<<c<<" d = "<<d<<endl;

}

?

int main()

{

    test01();

    return 0;

}

?

运行结果:

总结:

  • 函敌模板利用关键字template
  • 使用函数模板有两种方式:自动类型推导、显示指定类型
  • 模板的目的是为了提高复用性,将类型参数化

?

?

2.函数模板注意事项

注意事项

  1. 自动推导类型,必须推导出一致的数据类型T,才可以使用
  2. 模板必须要确定出T的数据类型,才可以使用

示例:

#include<iostream>

using namespace std;

// 注意事项

// 1. 自动推导类型,必须推导出一致的数据类型T,才可以使用

template<class T> // typename 可以替换成class

void my_Swap(T &a,T &b)

{

    T temp =a;

??? a=b;

??? b=temp;

}

void test01()

{

    int a = 10;

??? int b = 20;

    char c = 'c';

?? ?// 必须是一致的数据类型才能使用

??? // 如果一个是int一个是char则使用不了

??? cout<<"自动--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;

??? my_Swap(a,b);

    // my_Swap(a,c);// 推到不出一致的T类型

??? cout<<"自动--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;

}

// 2. 模板必须要确定出T的数据类型,才可以使用

template<typename T>

void func()

{

    cout<<"func函数调用"<<endl;

}

void test02()

{

??? // 可以强制给他一个类型,才可以正常调用

    // 因为func是一个模板,要给T初始类型才能调用

??? func<int>();

}

int main()

{

    test01();

??? test02();

??? return 0;

}

?

运行结果:

?

3.函数模板案例

案例说明:

  • 利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
  • 排序规则从大到小,排序算法为选择排序
  • 分别利用char数组和int数组进行测试

示例:

#include<iostream>

using namespace std;

?

// 交换的模板

template<typename T>

void func(T &a,T &b)

{

    T temp =a;

    a=b;

    b=temp;

}

?

// 实现对数组的排序

template<typename T>

void Sort(T arr[],int len)

{

    // 选择排序

    for(int i=0;i<len;i++){

        int max=i; // 认定第一个数就是最大值

        for(int j=i+1;j<len;j++){

            if(arr[max]<arr[j]){

                max=j; // 更新最大值的下标

            }

        }

        if(max!=i){

            func(arr[max],arr[i]);

        }

    }

}

?

// 打印的模板

template<typename T>

void print_arr(T arr[],int len)

{

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

    {

        cout<<arr[i]<<" ";

    }

    cout<<endl;

}

void test01()

{

    char char_arr[] = "abcdefghijklmnopqrstuvwxyz";

    int num = sizeof(char_arr) / sizeof(char);

    cout << "排序前的顺序为:" << endl;

    print_arr(char_arr, 26);

    Sort(char_arr, 26);????????

    cout << "从大到小排序后的顺序为:" << endl;

    print_arr(char_arr, 26);

}

void test02()

{

    int arr[] = { 7,8,3,4,6,5 };

    int num = sizeof(arr) / sizeof(int);

    cout << "排序前的顺序为:" << endl;

    print_arr(arr, num);

    Sort(arr, num);

    cout << "从大到小排序后的顺序为:" << endl;

    print_arr(arr, 6);

}

int main()

{

    test01();

    test02();

    return 0;

}

运行结果:

?

三、普通函数和模板函数的区别

普通函数和函数模板的区别:

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

示例:

#include<iostream>

using namespace std;

?

//? 普通函数调用会发生隐式类型的转换

int add(int a,int b)

{

    return a+b;

}

?

// 函数模板

template<typename T>

int my_ADD(T a,T b)

{

    return a+b;

}

?

void test01()

{

    int a=10;

    int b=20;

    char c='a'; // a=97
    
    // 普通类型的函数会发生隐式类型转化

    cout << "自动类型推导" << endl;

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

    cout<<char(add(a,c))<<endl;

?

    // 自动类型推导? 不会发生隐式类型转换

    // 必须是相同的类型,一个int,一个char他不会计算

    cout << "自动类型推导" << endl;

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

    // 显示指定类型 会发生隐式类型转换
    
    cout << "显示指定类型 会发生隐式类型转换" << endl;

    cout << char(my_ADD<char>(a, c)) << endl;

    cout<<my_ADD<int>(a,c)<<endl;

}

int main ()

{

    test01();

?

    return 0;

}

运行结果:

总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T

四、普通函数和模板函数的调用规则

调用规则:

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

示例:

#include<iostream>

using namespace std;

void my_print(int a,int b)

{

    cout<<"调用普通函数"<<endl;

}

template<typename T>

void my_print(T a,T b)

{

    cout<<"模板函数调用"<<endl;

}

?

// 函模板重载

template<typename T>

void my_print(T a,T b,T c)

{

    cout<<"重载--函数模板调用"<<endl;

}

void test01()

{

    int a=10;

    int b=20;

    // 1. 如果函数模板和普通函数都可以实现,优先调用普通函数

    // 即使普通函数只有定义没有实现,只会报错,不会调用模板函数

    my_print(a,b);

    my_print(a,b);

?

    // 2. 通过空模板参数列表,强制调用函数模板

    cout<<"通过空模板参数列表,强制调用函数模板"<<endl;

    my_print<>(a,b);

?

    // 3. 重载函数模板调用

???? my_print(a,b,100);

?

???? // 4.如果函数模板可以产生更好的匹配,优先调用函数模板

???? char c1 = 'a';

???? char c2 = 'b';

    cout << "更好的匹配" << endl;

???? my_print(c1,c2);

?}

?int main()

?{

    test01();

    return 0;

?}

运行结果:

五、模板的局限性

模板并不是万能的,有些特定数据类型,需要用具体化方式实现。

利用具体化的模板可以实现自定义类型的通用化

学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

不支持数组之间的赋值操作

Template<Typename T>

void f(T a, T b)

{

a=b;

}

?

不支持自定义类型

提供重载函数的方式,可以为特定类型提供具体的模板

Template<typename T>

void f(T a,T b)

{

if(a>b)

...

}

解决方法

示例:

#include<iostream>

using namespace std;

class person

{

public:

    person(string name,int age)

    {

        this->name=name;

        this->age=age;

    }

    string name;

    int age;

};

// 对比两个数据是否相等

template<typename T>

bool my_compare(T &a,T &b)

{

    if(a==b)

        return true;

    else

        return false;

}

?

// 利用具体化的对象实现,具体优化先调用

template<> bool my_compare(person &p1,person &p2)

{

    if(p1.name==p2.name&&p1.age==p2.age)

        return true;

    else

        return false;

}

?

void test01()

{

    int a = 10;

    int b =20;

    bool ret = my_compare(a,b);

    if(ret)

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

    else

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

}

// person数据类型模板不认识

void test02()

{

    person p1("Tom",18);

    person p2("Jack",20);

    bool ret = my_compare(p1,p2);

    if(ret)

        cout<<"p1==p2"<<endl;

    else

        cout<<"p1!=p2"<<endl;

}

int main()

{

    test01();

    test02();

    return 0;

}

运行结果:

?

文章来源:https://blog.csdn.net/hjl011006/article/details/135727228
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。