C 中类模板

C++中类模板

类模板(class Template)函数模板定义的类型参数可以用在函数声明和函数定义中,类模板中定义的类型参数可以用在类声明和类实现中。类模板的目的同样是将数据的类型参数化。

声明类模板:

template类名{

    //TODO;

}

类模板和函数模板都是以 template 开头(当然也可以使用 class,目前来讲它们没有任何区别),后跟类型参数;类型参数不能为空,多个类型参数用逗号隔开。一但声明了类模板,就可以将类型参数用于类的成员函数和成员变量了。换句话说,原来使用 int、float、char 等内置类型的地方,都可以用类型参数来代替。

区分类模板与模板类的概念

  一个类模板(类生成类)允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。

  如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。

#include 
#include
using namespace std;
template
class Test{
	public:
		void add(T1 a,T2 b){
			cout<<"void add(T1 a,T2 b)"<
class Test{
	public:
		void add(T1* a,T2* b){
			cout<<"void add(T1* a,T2* b)"<
class Test{  //Test类模板的两个类型参数完全相同时,使用这个实现 
	public:
		void add(T a,T b){
			cout<<"void add(T a,T b)"<"<//没有泛指类型
class Test{
	public:
		void add(void* a,void* b){
			cout<<"void add(void* a,void* b)"< t1;  // 使用第一个类模板;
    Test t2;  // 使用第三个类模板,特化实现;
    Test t3;  // 使用第四个类模板,特化实现;
    t1.add(1, 2.5);  // void add(T1 a, T2 b) 3.5;
    t2.add(5, 5);  // void add(T a, Tb) 10;
    t2.print();  // class Test < T, T >
    t3.add(NULL, NULL);  // void add(void* a, void* b);Error to add void* param...;
    Test t4;  // 未有定义指针特化时,编译器显示 14 行:error: invalid operands of types 'int*' and 'double*' to binary 'operator+';
                     // 特化指针后,打印 void add(T1* a, T2* b);
    int a = 1;
    double b = 0.1;
    t4.add(&a, &b);  // 1.1
    return 0;
}

1,类模板的特化实现表象上面好像定义了不同的类模板,但其实我们仅仅是根据需要将一个类模板分开成不同的情况来实现;

2,编译器编译过后根据我们使用的类型参数来决定究竟是哪一种实现;

类模板三种特化:

模板参数的类模板特化的几种类型, 一是特化为绝对类型; 二是特化为引用,指针类型;三是特化为另外一个类模板。

template
class Compare{
    public:
        static bool IsEqual(const T& lh,const T& rh){
            return lh==rh;
    }
};

(一)特化为绝对类型

也就是说直接为某个特定类型做特化,这是我们最常见的一种特化方式, 如特化为float, double等

template<>
class Compare{
    public:
        static bool IsEqual(const float& lh,const float& rh){
            return abs(lh-rh)<10e-3;
    }
};
template<>
class Compare{
    public:
        static bool IsEqual(const double& lh,const double& rh){
            return abs(lh-rh)<10e-6;
    }
};

(二)特化为引用,指针类型

这种特化我最初是在stl源码的的iterator_traits特化中发现的

template 
struct iterator_traits {
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

// specialize for _Tp*
template 
struct iterator_traits<_Tp*> {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef _Tp*                        pointer;
  typedef _Tp&                        reference;
};

// specialize for const _Tp*
template 
struct iterator_traits {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef const _Tp*                  pointer;
  typedef const _Tp&                  reference;
};

除了T*, 我们也可以将T特化为 const T*, T&, const T&等,以下还是以T*为例:

template
class Compare{
    public:
        static bool IsEqual(const T* lh,const T* rh){
            return Compare::IsEqual(*lh,*rh);
    }
};

特化其实是就不是一种绝对的特化, 它只是对类型做了某些限定,但仍然保留了其一定的模板性,这种特化给我们提供了极大的方便, 如这里, 我们就不需要对int*, float*, double*等等类型分别做特化了。

(三)特化为另外一个类模板

第二种方式的扩展,其实也是对类型做了某种限定,而不是绝对化为某个具体类型

template
class Compare >
{
public:
    static bool IsEqual(const vector& lh, const vector& rh)
    {
        if(lh.size() != rh.size()) return false;
        else
        {
            for(int i = 0; i < lh.size(); ++i)
            {
                if(lh[i] != rh[i]) return false;
            }
        }
        return true;
    }
};

把IsEqual的参数限定为一种vector类型, 但具体是vector还是vector, 我们可以不关心, 因为对于这两种类型,我们的处理方式是一样的,我们可以把这种方式称为“半特化”。

将其“半特化”为任何我们自定义的模板类类型:

template  
struct SpecializedType
{
    T1 x1;
    T1 x2;
};
template 
class Compare >
{
public:
    static bool IsEqual(const SpecializedType& lh, const SpecializedType& rh)
    {
        return Compare::IsEqual(lh.x1 + lh.x2, rh.x1 + rh.x2);
    }
};

三种类型的模板特化, 我们可以这么使用这个Compare类:

// double
    double d1 = 10;
    double d2 = 10;
    bool r3 = Compare::IsEqual(d1, d2);

    // pointer
    int* p1 = &i1;
    int* p2 = &i2;
    bool r4 = Compare::IsEqual(p1, p2);

    // vector
    vector v1;
    v1.push_back(1);
    v1.push_back(2);

    vector v2;
    v2.push_back(1);
    v2.push_back(2);
    bool r5 = Compare >::IsEqual(v1, v2);

    // custom template class 
    SpecializedType s1 = {10.1f,10.2f};
    SpecializedType s2 = {10.3f,10.0f};
    bool r6 = Compare >::IsEqual(s1, s2);

类模板特化注意:

1.特化只是模板的分开实现

本质上是同一个类模板,仅仅是将模板根据需要分开实现

2.特化类模板的使用方式是同一的

必须显示指定每一个类型参数

特化与重定义:

一个类模板和一个新类(或者两个类模板)

重定义本质是要么是实现了两个类模板,要么是一个类模板加上 一个新的类;

特化本质是只实现同一个类模板,特化的目的仅仅是考虑一些特殊的情况类模板应该如何工作

使用的时候没有统一的方式,要选择用类模板的种类或用新的类;

特化:

以统一的方式使用类模板和特化类;编译器自动优先选择特化类;能用特化就不要重定义;函数模板只支持类型参数完全特化:

#include
#include 
using namespace std;
/* 以下是函数模板的特化实验 */ 

template
< typename T >
bool Equal(T a, T b)
{
    cout << "bool Equal(T a, T b)" << endl;
    
    return a == b;
}

/* 函数完全特化解决浮点数比较问题 */
template
< >
bool Equal(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    
    cout << "bool Equal(double a, double b)" << endl;
    
    return (-delta < r) && (r < delta);
}

/* 直接重载 */
bool Equal(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    
    cout << "bool Equal(double a, double b)" << endl;
    
    return (-delta < r) && (r < delta);
}

int main()
{  
    Test t3;  // 这里错误了,要用 Test_Void t3; 这样的 定义方式,因为重定义了类的实现方式,注销了模板特化方式;写代码时,要时刻考虑究竟是要使用类模板 还是要使用新类,这就是弊端,所以能特化时,就不要重新定义、重新实现;
                            
    cout << Equal( 1, 1 ) << endl;  // bool Equal(T a, T b) 1
    cout << Equal<>( 0.001, 0.001 ) << endl;  // 用相等符号比较两个浮点数是否相等是有问题的;用了特化后:bool Equal(double a, double b) 1
    cout << Equal( 0.001, 0.001 ) << endl;  // bool Equal(double a, double b) 1;这里调用全局重载函数,因为编译器会优先寻找全局重载函数;
                                            
    return 0;
}

当需要重载函数模板时,优先考虑使用模板特化;当模板特化无法满足需求,再使用函数重载!

小结:

1.类模板可以定义任意多个不同的类型参数;

2.类模板可以被部分特化和完全特化;

3.特化的本质是模板的分开实现;

4.函数模板只支持完全特化;

5.工程中使用模板特化代替类(函数)重定义;

全特化与部分特化

template
class Test
{
public:
    Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<
class Test
{
public:
    Test(int i, char j):a(i),b(j){cout<<"全特化"<
class Test
{
public:
    Test(char i, T2 j):a(i),b(j){cout<<"部分特化"< t1(0.1,0.2);  //类模板
Test t2(1,'A');//全特化
Test t3('A',true);//部分特化

函数不存在部分特化的功能,可以通过函数的重载完成。

函数模板作为类模板成员

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

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

 

你可能感兴趣的:(C 中类模板)