C++模板的目的就是实现泛型,泛型最大的作用就是不用给不同的数据类型写相同的代码, 不同的数据类型都可以使用同一套代码。使用模板时,我们只需要将具体的数据类型作为参数传给模板,这样编译器在编译时会像宏替换一样将模板上的占位符替换成指定的数据类型,与宏展开不同的是,在模板展开前,编译器会先做类型检查。

指定了具体数据类型的模板都会被展开。即使模板的源码只包含方法或类,但是编译后的代码可能会包括多份同样方法或类的的代码。

举个例子来说明模板:比如说很多数据类型都需要max()求最大值的方法,那么我们可以写一个max()的方法,然后接受一个数据类型的参数,从而为不同数据类型提供求最大值的方法,而不是为不同的数据类型都实现同样的方法。

C++提供了两个关键字来支持模板的实现:template和typename,其中typename常常被class关键所替换。


template <typename T>
T max(T a, T b){
    return (a > b) ? a : b;
}

int main(int argc, char *argv[])
{
    ...
    qDebug() << max<int>(10,30) << Qt::endl;
    qDebug() << max<double>(11.0,30.9) << Qt::endl;
    qDebug() << max<float>(13.40f,30.7f) << Qt::endl;
    qDebug() << max<char>('a','b') << Qt::endl;
    return a.exec();
}

上面这个就是函数模板啦,typename关键字还可以换成class关键字:

template <class T>
T max(T a, T b){
    return (a > b) ? a : b;
}

我们再举一个冒泡排序:


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

template <typename T>
void bubbleSort(T a[], int n){
    for(int i = 0; i < n-1; i++){
        for(int j = n -1; i < j;j--){
            if(a[j] < a[j-1]){
                swap1(a[j],a[j-1]);
            }
        }
    }
}
l
int main(int argc, char *argv[])
{
    ...

    int data[5] = { 60,30,50,40,20 };
    bubbleSort<int>(data,5);
    for(int i = 0;i<5;i++){
        qDebug() << data[i] << Qt::endl;
    }

    ...
    return a.exec();
}

函数模板就是Java中的泛型方法,上面就是函数模板的一些实例,现在我们来看一下类模板,它就相当于Java中的泛型类。 对于类模板,我们可以定义一些独立于具体数据类型的东西,像List,Array,LinkedList, BinaryTree, Stack, Queue这些集合类型,就非常适合用类模板来定义了,如:


template <typename T>
class Array {
private:
    T* ptr;
    int size;
public:
    Array(T arr[],int size);
    void print();
};

template <typename T>
Array<T>::Array(T arr[], int size){
    ptr = new T[size];
    this->size = size;
    for(int i = 0;i < size; i++){
        ptr[i] = arr[i];
    }
}

template <typename T>
void Array<T>::print(){
    for(int i = 0; i < size; i++){
        qDebug() << "" << *(ptr + i);

    }
    qDebug() << Qt::endl;
}



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

    int arr[5] = { 12,22,33,44,55};
    Array<int> ar(arr,5);
    ar.print();


    ...
    return a.exec();
}

对于模板的数据类型参数,我们也可以给定默认值,如:

template <class T, class U = char> class A {
public:
    T x;
    U y;
    A() { cout << "Constructor Called" << endl; }
};
  
int main()
{
    A<char> a; // This will call A<char, char>
    return 0;
}

在模板的数据类型参数位置,我们也可以给非数据类型参数,如:

template <typename T , int max> // int max 非数据类型参数
int minOfArr(T arr[], int n){
    int m = max;
    for(int i = 0;i< n; i++){
        if(arr[i] < m){
            m = arr[i];
        }
    }
    return m;
}



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

    int arr[5] = { 12,22,33,44,55};
    
    qDebug() << minOfArr<int,30>(arr,5) << Qt::endl;
    return a.exec();
}

特化,所谓的特化,本来对所有数据类型都有同样的模板代码逻辑,但是有个别的类型要特殊处理下,那么这就是模板特化了,如:

template <class T>
void fun(T a)
{
   cout << "The main template fun(): "
        << a << endl;
}
 
template<>
void fun(int a)//对int整型进行特殊处理
{
    cout << "Specialized Template for int type: "
         << a << endl;
}

int main(int argc, char *argv[])
{
    ...
    fun<char>('a');
        fun<int>(10);

    return a.exec();
}

上面是函数模板的特化,下面是类模板的特化:

template <class T>
class Test
{
  // Data members of test
public:
   Test()
   {
       // Initialization of data members
       cout << "General template object \n";
   }
   // Other methods of Test
};
 
template <>
class Test <int> // 对int整型进行特殊处理
{
public:
   Test()
   {
       // Initialization of data members
       cout << "Specialized template object\n";
   }
};

如果存在某个数据类型的专门化版本,编译器首先检查专门化版本,然后检查主模板。编译器首先检查专门化的版本,将传递的参数与专门化版本中指定的数据类型进行匹配。

总结一下

函数模板的语法:

template <class T>
<return-type> <function-name> (  <parameters of type T> )
{
                 //function body
}

类模板的语法:

template <class T>
class <class-name>
{
   //class declaration
}

template和class是关键字,class关键字可以用typename代替。尖括号(<class T>)中的参数可以是数据类型或非数据类型参数,甚至可以设置默认值。