C++ 面向对象编程7 模板

概念

  模板作用强大且复杂,由于水平有限本文只做简单讲解和使用。模板的作用就是实现类型通用,降低代码的冗余度模板可以为一种算法定义适用不同类型的版本,实现机制:

1.使用类型参数摆脱类型的限制,丧失了一定的类型安全;
2.模板要实例化才能使用,实例化由编译器来实现的。

分类

函数模板

  函数模板就是带类型参数的函数,函数的返回值,局部变量,形式参数都可以使用类型参数,函数模板支持类型推断(形参)。

函数模板 ---> 实例化 ---> 函数
             编译

类模板

  类模板就是带类型参数的类,类的成员变量,成员函数,成员类型都可以使用类型参数,类模板不支持类型推断。

类模板 ---> 实例化 ---> 类 ---> 实例化 ---> 对象
           编译                运行

使用

函数模板

  语法:

//函数模板定义
template<typename T/*类型参数*/....>
返回值类型  函数模板名(形参列表)
{
    .......//函数中可以使用类型参数T作为类型
}

//函数模板的调用
函数模板名<类型...>(实参);//如果类型参数可以通过实参推断出来,传递的类型参数可以省略

类模板

  语法:

//类模板的定义
template<typename T/*类型参数*/....>
class 类模板名{
    ........//类的成员可以使用类型参数T作为类型    
};

//类模板的使用
类模板名<类型...> 对象名; //类模板不支持类型推断

模板的特化

  如果模板对某些特殊的类型的行为需要重新定义,此时可以进行模板特化;比如创建一个用于比较大小的函数模板,无论是int型还是float型、char型都可以通用,但是对于char*(字符串),是不能使用这个模板的,所以需要对这个模板进行特化,当传入的参数是char *则调用重新定义的模板,而不是使用之前通用类型的模板,对于类模板也是一样,因为有一些类型的运算和通用模板不符也需要进行特化,语法如下:

//函数模板特化
template<>
返回值类型  函数模板名<特化的类型...>(形参列表)
{
    .....//使用特化的类型
}

//类模板特化
template<>
class 类模板名<特化的类型...>{
    ........//类的成员使用特化类型作为类型    
};

  编译器在对模板实例化时,如果原模板和特化模板同时符合,优先选择特化的模板。

实验程序:

#include <iostream>
#include <string>
#include <cstring>

using namespace std;

//函数模板
template <typename T>
T mymax(const T a, const T b)
{
    return a > b ? a : b;
}

//函数模板特化
template<>
const char * mymax<const char *>(const char *str1, const char *str2)
{
    return strcmp(str1, str2) > 0 ? str1 : str2;
}

//类模板
template <typename T>
class comparator{
public:
    comparator(T a, T b):a(a),b(b){};
    T max()
    {
        return a > b ? a : b;
    }
    T min()
    {
        return a > b ? b : a;
    }

private:
    T a;
    T b;
};

//类模板特化
template<>
class comparator<const char* >{
public:
    comparator(const char *str1, const char *str2):str1(str1),str2(str2){};
    const char *max()
    {
        return strcmp(str1, str2) > 0 ? str1 : str2;
    }

private:
    const char *str1;
    const char *str2;
};

int main()
{
    const char *str1 = "abc";
    const char *str2 = "efg";
    
    cout<<"int max:"<<mymax<int>(99, 100)<<endl;
    cout<<"float max:"<<mymax<float>(0.000001, 0.000002)<<endl;
    cout<<"const char *:"<<mymax<const char *>(str1, str2)<<endl;

    comparator<int> c1(100, 200);
    cout<<"class int max:"<<c1.max()<<endl;
    cout<<"class int min:"<<c1.min()<<endl;

    comparator<float> c2(0.001, 0.002);
    cout<<"class float max :"<<c2.max()<<endl;
    cout<<"class float min :"<<c2.min()<<endl;

    comparator<const char *> c3("abc", "efg");
    cout<<"class const char * max:"<<c3.max()<<endl;

    return 0;
}

输出结果:

类模板的成员特化

  对于类模板而言,既可以进行全类特化,也可以只针对部分与类型相关的成员函数进行特化。成员特化时要保证接口的规则和原通用模板完全一致,语法如下:

template<>
返回值类型 类模板名<特化类型...>::成员函数名(形参列表)
{
    ........//使用特化的类型
}

实验程序:排序

#include <iostream>
#include <cstring>
#include <string>

using namespace std;

template<typename T>
class sortor{
public:
    sortor(T *a, int n)
    {
        this->a = new T[n];
        this->n = n;

        for(int i = 0; i < n; i++)
        {
            this->a[i] = a[i];
        }
    }
    ~sortor()
    {
        delete[] this->a;
    }
    void sort()
    {
        int i;
        int j;

        for(i = 0; i < n-1; i++)
        {
            bool flag = true;
            for(j = 0; j < n - 1 - i; j++)
            {
                if (a[j]>a[j+1])
                {
                    flag = false;
                    T tmp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = tmp;
                }
            }
            //扫描一次没有交换,说明已经排好序了
            if (flag)
                break;
        }
    }

    void print()
    {
        for(int i=0;i<n;i++)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }

private:
    T *a;   //首地址
    int n;  //长度
};

//成员特化
template<>
void sortor<const char *>::sort()
{
    int i;
    int j;

    for(i = 0;i < n - 1; i++)
    {
        bool flag = true;
        for(j = 0; j < n - 1 - i; j++)
        {
            if(strcmp(a[j], a[j + 1]) > 0)
            {
                flag = false;
                const char *tmp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = tmp; 
            }
        }
        //扫描一次没有交换,说明已经排序好了
        if(flag)
            break;
    }
};

int main()
{
    int arr[] = {5,9,6,8,4,7,1,17,2};
    double d_arr[] = {5.9,6.8,4.0,7.5,3.6,8.9,2.4,9.7,1.3};
    string str_arr1[] = {"ddd","ccc","bbb","eee","aaa"};
    const char *str_arr2[] = {"ddd","ccc","bbb","eee","aaa"};


    sortor<int> s1(arr, sizeof(arr)/sizeof(arr[0]));
    s1.sort();
    s1.print();

    sortor<string> s2(str_arr1,sizeof(str_arr1)/sizeof(str_arr1[0]));
    s2.sort();
    s2.print();

    sortor<const char *> s3(str_arr2, sizeof(str_arr2)/sizeof(str_arr2[0]));
    s3.sort();
    s3.print();

    return 0;
}

输出结果:

类模板的局部特化

  对于具有多个类型参数的类模板,可以只特化其中一部分类型参数,编译器优先选择特化程度最高的版本。

1.特化一部分类型参数

template<T1,T2,T3>
class xxxxx{};

//局部特化
template<T1,T2>
class xxxxx<T1,T2,int>{};

tempalte<T1>
class xxxxx<T1,char,int>{}

2.特化类型参数的特殊关系

template<T1,T2,T3>
class xxxxx{};

//局部特化
template<T1,T2>
class xxxxx<T1,T2,T2>{};

3.特化指针和数组类型

template<T1,T2,T3>
class xxxxx{};

//局部特化
template<T1,T2,T3>
class xxxxx<T1*,T2*,T3*>{};

实验程序

#include <iostream>
#include <string>

using namespace std;

//通用类模板特化
template<typename T1, typename T2, typename T3>
class trib{
public:
    trib()
    {
        cout<<"trib<T1, T2, T3>"<<endl;
    }
};

//局部特化
//1.特化某些类型参数
template<typename T1, typename T2>
class trib<T1, T2, int>{
public:
    trib()
    {
        cout<<"trib<T1, T2, int>"<<endl;
    }
};

//2.特化类型参数的关系
template<typename T1, typename T2>
class trib<T1, T2, T2>{
public:
    trib()
    {
        cout<<"trib<T1, T2, T2>"<<endl;
    }
};

//3.特化数组/指针类型
template<typename T1, typename T2, typename T3>
class trib<T1*, T2*, T3*>{
public:
    trib()
    {
        cout<<"trib<T1*,T2*,T2*>"<<endl;
    }
};

//全特化
template<>
class trib<int, char, int>{
public:
    trib()
    {
        cout<<"Trib<int,char,int>"<<endl;
    }
};

int main()
{
    trib<int, double, char> t1;//通用
    trib<double, char, int> t2; //局部特化
    trib<double, double, int> t3; //局部特化
    trib<int, double, double> t4; ////特殊关系特化
    trib<int,char,int> t5; //全特化
    trib<int *,double *,char *> t6; //特化数组/指针

    return 0;
}

输出结果:

类模板的默认参数

  1.类模板的类型参数可以有默认值,有默认值的类型参数要放到右边,如果实例化时不提供参数的类型,就使用默认值;
  2.后面类型参数的默认值可以使用前面类型参数的值;
  3.非类型参数也可以有默认值。

实验程序:

#include <iostream>
#include <typeinfo>

using namespace std;

//类模板的默认参数
template <typename T1=int, typename T2=char, int T3=1000>
class A{
public:
    static void print_type()
    {
        cout<<typeid(T1).name()<<" "
            <<typeid(T2).name()<<" "
            <<T3<<endl;
    }
};

template<typename T1, typename T2=T1>
class B{
public:
    static void print_type()
    {
        cout<<typeid(T1).name()<<" "
            <<typeid(T2).name()<<endl;        
    }
};

int main()
{
    const int num = 10;

    A<int, int, num>::print_type();
    A<double, short>::print_type();
    A<string>::print_type();
    A<>::print_type();

    B<double>::print_type();

    return 0;
}

输出结果:

智能指针

  将类类型传入模板,构成智能指针

实验程序:

#include <iostream>

using namespace std;

class A{
public:
    A(){cout<<"A()"<<endl;}
    ~A(){cout<<"~A()"<<endl;}

    void show()
    {
        cout<<"showA()"<<endl;
    }
};

//模板化指针对象
template<typename T>
class myauto_ptr{
public:
    myauto_ptr(T *p=NULL):pdata(p)
    {

    }
    myauto_ptr(myauto_ptr<T> &t) //自定义类类型将调用这个构造函数
    {
        this->pdata = t.data;
        t.pdata = NULL;
    }

    ~myauto_ptr()
    {
        if(this->pdata)
        {
            delete this->pdata;
            this->pdata = NULL;
        }
    }
    //重载->运算符
    T *operator->()
    {
        return this->pdata;
    }

    //重载*运算符
    T& operator*()
    {
        return *this->pdata;
    }
private:
    //要管理的指针
    T *pdata;
};

int main()
{
    int *p_int = new int(10);
    myauto_ptr<int> pi(p_int);
    cout<<*pi<<endl;

    A *p_a = new A;
    myauto_ptr<A> pa(p_a);
    pa->show();
    (*pa).show();

    return 0;
}

输入结果:

待补充:

模板的继承
模板的模板成员
模板的模板参数
posted @ 2021-03-29 00:36  ding-ding-light  阅读(115)  评论(0编辑  收藏  举报