17. C++类模板

在C++中我们往往需要编写多个形式和功能都相似的类,于是 引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复工作。

17.1 类模板基本格式

C++ 中类模板的写法如下:

template <类型参数表>
class 类模板名{
    成员函数和成员变量
};

类型参数表的写法如下:

class类塑参数1, class类型参数2, ...

类模板中的成员函数放到类模板定义外面写时的语法如下:

template <类型参数表>
返回值类型  类模板名<类型参数名列表>::成员函数名(参数表)
{
    ...
}

用类模板定义对象的写法如下:

类模板名<真实类型参数表> 对象名(构造函数实际参数表);

如果类模板有无参构造函数,那么也可以使用如下写法:

类模板名 <真实类型参数表> 对象名;

类模板看上去很像一个类。下面以 Pair 类模板为例来说明类模板的写法和用法。

实际中,某项数据记录由两部分组成,一部分是关键字,另一部分是值。关键字用来对记录进行排序和检索,根据关键字能查到值。如,学生记录由两部分组成,一部分是学号,另一部分是绩点。要能根据学号对学生进行排序,以便方便地检索绩点,则学号就是关键字,绩点就是值。
例如:

#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
public:
    T1 key;  //关键字
    T2 value;  //值
    Pair(T1 k,T2 v):key(k),value(v) { }
    bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
    return key < p.key;
}
int main()
{
    Pair<string,int> student("哈哈",23); //实例化出一个类 Pair<string,int>
    cout << student.key << " " << student.value;
    return 0;
}

《17. C++类模板》 image.png

17.2 函数模板作为类模板成员

类模板中的成员函数还可以是一个函数模板。成员函数模板只有在被调用时才会被实例化。例如下面的程序:

#include <iostream>
using namespace std;
template <class T>
class A
{
public:
    template <class T2>
    void func(T2 t) { cout << t; }  //成员函数模板
};
int main()
{
    A<int> a;
    a.func('K');  //成员函数模板Func被实例化
    a.func("hello");
    return 0;
}

17.3 C++用类模板实现可变长数组

#include <iostream>
#include <cstring>
using namespace std;
template <class T>
class Array
{
    int size; //数组元素的个数
    T *ptr; //指向动态分配的数组
public:
    Array(int s = 0);  //s代表数组元素的个数
    Array(Array & a);
    ~Array();
    void push_back(const T & v); //用于在数组尾部添加一个元素v
    Array & operator=(const Array & a); //用于数组对象间的赋值
    T length() { return size; }
    T & operator[](int i)
    {//用以支持根据下标访问数组元素,如a[i] = 4;和n = a[i]这样的语句
        return ptr[i];
    }
};
template<class T>
Array<T>::Array(int s):size(s)
{
     if(s == 0)
         ptr = NULL;
    else
        ptr = new T[s];
}
template<class T>
Array<T>::Array(Array & a)
{
    if(!a.ptr) {
        ptr = NULL;
        size = 0;
        return;
    }
    ptr = new T[a.size];
    memcpy(ptr, a.ptr, sizeof(T ) * a.size);
    size = a.size;
}
template <class T>
Array<T>::~Array()
{
     if(ptr) delete [] ptr;
}
template <class T>
Array<T> & Array<T>::operator=(const Array & a)
{ //赋值号的作用是使"="左边对象里存放的数组,大小和内容都和右边的对象一样
    if(this == & a) //防止a=a这样的赋值导致出错
    return * this;
    if(a.ptr == NULL) {  //如果a里面的数组是空的
        if( ptr )
            delete [] ptr;
        ptr = NULL;
        size = 0;
        return * this;
    }
     if(size < a.size) { //如果原有空间够大,就不用分配新的空间
         if(ptr)
            delete [] ptr;
        ptr = new T[a.size];
    }
    memcpy(ptr,a.ptr,sizeof(T)*a.size);
    size = a.size;
     return *this;
}
template <class T>
void Array<T>::push_back(const T & v)
{  //在数组尾部添加一个元素
    if(ptr) {
        T *tmpPtr = new T[size+1]; //重新分配空间
    memcpy(tmpPtr,ptr,sizeof(T)*size); //拷贝原数组内容
    delete []ptr;
    ptr = tmpPtr;
}
    else  //数组本来是空的
        ptr = new T[1];
    ptr[size++] = v; //加入新的数组元素
}
int main()
{
    Array<int> a;
    for(int i = 0;i < 5;++i)
        a.push_back(i);
    for(int i = 0; i < a.length(); ++i)
        cout << a[i] << " ";
    return 0;
}

《17. C++类模板》 image.png

注意:
1、类模板中可以定义静态成员,从该类模板实例化得到的所有类都包含同样的静态成员。

#include <iostream>
using namespace std;
template <class T>
class A
{
private:
    static int count;
public:
    A() { count ++; }
    ~A() { count -- ; }
    A(A &) { count ++ ; }
    static void PrintCount() { cout << count << endl; }
};

/*
下面也可以这样写
int A<int>::count = 0;
int A<double>::count = 0;

*/

template<> int A<int>::count = 0;
template<> int A<double>::count = 0;


int main()
{
    A<int> ia;
    A<double> da;
    ia.PrintCount();
    da.PrintCount();
    return 0;
}

《17. C++类模板》

2、类模板的“类型参数表”中可以出现非类型参数

#include <iostream>
using namespace std;
template<class T, int size>
class Array {
T array[size];
public:
    void Print()
    {
        for (int i = 0; i < size; ++i)
        cout << array[i] << endl;
    }
};

int main()
{

    return 0;
}

可以用 CArray 模板定义对象

Array<int, 40> a;

编译器自动生成名为 CArray<int, 40> 的类。该类是通过将 CArray 模板中的 T 换成 int、 size 换成 40 后得到的。

还可定义以下对象:

Array <double, 40> a2;
Array <int, 50> a3;

注意,Array<double, 40> 和 Array<int, 50> 完全是两个类,这两个类的对象之间不能互相赋值。

    原文作者:小Q_wang
    原文地址: https://www.jianshu.com/p/bc2fbb6136c0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞