C++ 模版类 详细讲解

来源:互联网 发布:日晷怎么看时间 知乎 编辑:程序博客网 时间:2024/06/05 08:07

模版类定义

直接先说一下模版类的定义:

template <模版类型1,模版类型2...>class 类名{  内容}

一个小示例如下:

    template<class T>    class A     {    private:        T data;    public:        A(T value) :data(value) {};        T GetData() { return this->data; }//模版函数,返回类型为T的值    };

模版类成员函数

以上的模版函数是在类的内部定义的,如果我们要定义一个定义在类外的类模版成员函数的话,我们应该将这样的类模版成员函数定义在类的.h文件一起,定义方法如下:

template <模版类型1,模版类型2...>返回类型 类名<模版类型1,模版类型2...>::函数名(参数1,参数2){  内容}

以下为一个示例:

template<class T>    class A     {    private:        T data;    public:        A(T value) :data(value) {};        T GetData() { return this->data; }//模版函数,返回类型为T的值        T Test() const;    };    template<class T>    T A<T>::Test() const    {        cout << "hello,template class" << endl;        return data;    }

当我们使用如下代码的时候:

A<int> intA(1);

这个时候,类模版会绑定到int类型上,然后使用int类型来实例化出特定的类,比如上面的代码就会实例化出与下面等价的类:

template<>    class A<int>     {    private:        int data;    public:        A(int value) :data(value) {};        int GetData() { return this->data; }//模版函数,返回类型为T的值        int Test() const ;    };    template<>    int A<int>::Test() const    {        cout << "hello,template class" << endl;        return data;    }

当编译器从我们的类模版A中实例化出一个类的时候,它会重写A模版,将摸板参数T的每一个实例都替换成给定的模版实参

如果一个成员函数没有被使用,它就不会被实例化。成员函数只有我们使用的时候才会实例化,当然,我们可以自己特化针对特定类型的成员函数

模版类代码区域内和代码区域外的不同模板名使用方法

在类模版自己的作用域下和在类外的模版使用方法是不同的
我写一个例子,还是如上的代码,模版类A,在类的内部写一个如下成员函数:

        A Test()         {            A a1= *this;            return a1;        }

这个成员函数是不用写成返回值为A<T>类型的,而只需要写成A,但若是在类外定义这个函数,就必须写成刚才上面所说的格式:

    template<class T>    A<T> A<T>::Test()     {        A a1 = *this;        return a1;    }

我们就必须使用完整的A<T> 作为返回值
不过在函数的内部,由于我们已经进入了类的作用域,所以在定义a1的时候,无需重复模版实参。原因如下:
在类的作用域内,如果我们不提供模版实参,则编译器将默认我们的类型与成员实例化所用类型一致

因此,a1的定义实际上如下:

A<T> a1 = *this ;

模版类型别名

我们可以定义一个typedef来引用一个实例化的类:

typedef A<int> intA ;

但是我们没办法定义一个typedef引用一个模版,即无法定义一个typedef 引用A<T>
但是,新标准允许我们为类模版定义一个类型别名:

template <class> using twin_p=pair<T,T>;twin<string> stringPair;//stringPair是一个pair<string,string>类型数据

模版类的static成员

与所有类一样,类模版也可以定义static成员:

template<class T>    class A     {    private:        T data;        static int static_value;    public:        A(T value) :data(value) {};        T GetData() { return this->data; }//模版函数,返回类型为T的值        static int GetValue()         {            return static_value;        }    };

在上面的代码里面,A是一个类模版,它有一个名为static_value 的static成员变量。这代表每一个A的实例都有一个名为static_valuestatic数据成员实例
即,对任何给定类型X,都有一个A<X>::static_value ,所有A<x> 共享同一个static成员
由于每一个模版实例都有其独有的static成员,所有static的定义也要定义为模版:

template<class T> int A<T>::static_value = 5;    //定义并且初始化static_value

同样,我们也可以通过实例类直接访问具体的static成员:

cout << A<int>::GetValue() << endl; //  5

和其他的成员函数类似,一个static的成员函数和成员变量也只有在使用的时候才会实例化。

模版默认实参

在新标准中,我们可以为函数和模版类提供默认实参,比如如下代码(比较两个数的大小,默认使用标准库 less<T> 函数对象模版 ):

    template<calss T,calss F=less<T>>    int compare(const T& v1, const T& v2, F f = F())    {        if (f(v1, v2))return -1;        if (f(v2, v1))return 1;        return 0;    }

我们就将这个less<T> 绑定到F的默认参数上,当我们不使用第三个参数的时候,它就默认使用less<T> 来进行比较:

    int i = compare(1, 5);  //使用less,i=-1    data<int> d1(1), d2(2);    bool j = compare(d1, d2, compareData);//自己写的比较函数模版

可以看到,我们可以自己写比较函数,也可以直接用默认的比较函数,标准库里的很多东西都是如此,套路深。
最后
观察如下代码:

    template<typename T = int>    class data    {    private:        T value;    public:        data(T v) :value(v) {};    };    data<double> doublueData(3.5);    data<>intData(4);  //空<>表示我们希望使用默认类型

我们可以给所有的模版参数都给定默认实参,且如果我们希望使用这些默认实参,我们就跟一个空尖括号对。

~结束