二、类模板( Class Templates)

1 类似于函数模板,类模板也是类型的参数化。

例子,在头文件中类模板声明和定义:

#include <vector> 
#include <stdexcept> 

template <typename T> 
class Stack { 
  private: 
    std::vector<T> elems;     // elements 

  public: 
    void push(T const&);      // push element 
    void pop();               // pop element 
    T top() const;            // return top element 
    bool empty() const {      // return whether the stack is empty 
        return elems.empty(); 
    } 
}; 
template <typename T> 
void Stack<T>::push (T const& elem) 
{ 
    elems.push_back(elem);    // append copy of passed elem 
} 

template<typename T> 
void Stack<T>::pop () 
{ 
    if (elems.empty()) { 
        throw std::out_of_range("Stack<>::pop(): empty stack"); 
    } 
    elems.pop_back();         // remove last element 
} 

template <typename T> 
T Stack<T>::top () const 
{ 
    if (elems.empty()) { 
        throw std::out_of_range("Stack<>::top(): empty stack"); 
    } 
    return elems.back();      // return copy of last element 

在上面的例子中,使用的C++标准库中std::vector<>,因此不需要手动实现内存管理、拷贝构造、赋值操作等函数,只需要实现类模板的接口。

1.1、类的声明和函数的声明类型,也是声明一个类型参数T。

template <typename T> 
class Stack { 
  … 
}; 

在类模板内,T做为任意的类型可以声明成员变量也可以声明成员函数。上面的例子中,声明了一个包含T类型的vector。这个类的类型是Stack,T是模板参数。当声明变量或函数是,都要写成Static。
例如,声明copy构造函数和assignment从操作:

template <typename T> 
class Stack { 
    … 
    Stack (Stack<T> const&);                 // copy constructor 
    Stack<T>& operator= (Stack<T> const&);   // assignment operator 
    … 
};

如果,只需要类型名而不需要类型T,那么只是有statck就可以了,例如类的构造函数和析构函数。

1.2、成员函数的定义
类模板成员函数的定义,必须指定类模板的成员函数是一个模板函数,因此需要类模板的全程。
如下,类模板Statc的成员函数push的定义:

template <typename T> 
void Stack<T>::push (T const& elem) 
{ 
    elems.push_back(elem);    // append copy of passed elem 
} 

2、类模板的使用

使用类模板时,需要显示的指定模板参数。
例如:

#include <iostream> 
#include <string> 
#include <cstdlib> 
#include "stack1.hpp" 

int main() 
{ 
    try { 
        Stack<int>         intStack;       // stack of ints 
        Stack<std::string> stringStack;    // stack of strings 

        // manipulate int stack 
        intStack.push(7); 
        std::cout << intStack.top() << std::endl; 

        // manipulate string stack 
        stringStack.push("hello"); 
        std::cout << stringStack.top() << std::endl; 
        stringStack.pop(); 
        stringStack.pop(); 
    } 
    catch (std::exception const& ex) { 
        std::cerr << "Exception: " << ex.what() << std::endl; 
        return EXIT_FAILURE;  // exit program with ERROR status 
    } 
} 

上面例子中声明了一个Stack, 类模板中参数表示符T被int所取代。所以intStack是一个对象,此对象的内部成员vetor容纳的元素类型是int,它所调用的任何成员方法都是使用int实例化。

只有那些被调用的成员函数才会被实例化。对于类模板而已,只有成员函数被使用时才会实例化,这样会节约时间和空间。另外一个好处是,如果不支持类模板中的某些操作,只要不去使用这些操作依然可以实例化类模板。
例如,某个类模板中有操作符“operator<”,用来对类中元素排序,如果某个类型不支持“<”操作,只要不调用“<”操作还是可以实例化类模板的。

使用关键词typedef,可以让类模板的使用更加方便。
如下:

typedef Stack<int> IntStack; 

void foo (IntStack const& s)   // s is stack of ints 
{ 
    IntStack istack[10];       // istack is array of 10 stacks of ints 
    … 
}

使用typedef定义了Stack的别名 IntStack。这两个是同样的类型,可以互换使用的。

模板参数可是任意的类型,如下:

Stack<float*>      floatPtrStack;  // stack of float pointers 
Stack<Stack<int> > intStackStack;  // stack of stack of ints 

既可以是float指针,也可以是Stack类型,唯一需要的是这些类型能够支持调用到的操作。

注:上面的定义中两个“>”之间必须要有空格,否则编译器会产生语法错误。

Stack<Stack<int>> intStackStack;  // ERROR: >> is not allowed 

3、模板特化(Specializations of Class Templates)

可以为类模板的某些模板参数进行特化。类模板的特化和函数模板的重载类似,类模板的特化可以针对某些类型优化它的实现,也可以修改类模板对某些类型实例化时出现的错误的实例化行为。如果,对类模板特化就需要特化类模板中的所有成员函数。如果只特化类模板中的某个成员函数,那么就不能特化整个类模板了。

特化类型模板使用关键字“template<>”声明,并且需要显示指定需要特化的类型,特化的类型写在类名之后,如下:

template<> 
class Stack<std::string> { 
  … 
}; 

上面例子中为特化了一个类型是std:: string类型的Stack类型。

特化的类中的成员函数的定义与普通函数类似,要将类型标识符T替换为特化的类型,如下:

void Stack<std::string>::push (std::string const& elem) 
{ 
    elems.push_back(elem);    // append copy of passed elem 
} 

4、偏特化(又称部分特化、局部特化)(Partial Specialization)

类模板可以偏特化。在特殊的情况下,可以对类模板实施部分特化,其余的类模板参数可以有用户指定。如下:

//类模板
template <typename T1, typename T2> 
class MyClass { 
  … 
};
//偏特化
// partial specialization: both template parameters have same type 
template <typename T> 
class MyClass<T,T> { 
  … 
}; 

// partial specialization: second type is int 
template <typename T> 
class MyClass<T,int> { 
  … 
}; 

// partial specialization: both template parameters are pointer types 
template <typename T1, typename T2> 
class MyClass<T1*,T2*> { 
  … 
};

类模板使用:

MyClass<int,float> mif;    // uses MyClass<T1,T2> 
MyClass<float,float> mff;  // uses MyClass<T,T> 
MyClass<float,int> mfi;    // uses MyClass<T,int> 
MyClass<int*,float*> mp;   // uses MyClass<T1*,T2*> 

如果使用类声明与多个类模板的偏特化都匹配的化,编译器因歧义而产生错误,如下:

MyClass<int,int> m;        // ERROR: matches MyClass<T,T> 
                           //        and MyClass<T,int> 
MyClass<int*,int*> m;      // ERROR: matches MyClass<T,T> 
                           //        and MyClass<T1*,T2*> 

针对上面例子中在那个的,第二个产生的歧义的,可以再定义一个偏特化的模板,模板参数是两个同类型的指针,如下:

template <typename T> 
class MyClass<T*,T*> { 
  … 
};

5、默认模板参数

可以为类模板的模板参数设置默认值,这些默认值被称为默认模板参数。默认值甚至可以引用前一个声明的类模板的默认模板参数。
如下,可以为类模板Stack<>的第二个参数指定默认值:

#include <vector> 
#include <stdexcept> 

template <typename T, typename CONT = std::vector<T> > 
class Stack { 
  private: 
    CONT elems;               // elements 

  public: 
    void push(T const&);      // push element 
    void pop();               // pop element 
    T top() const;            // return top element 
    bool empty() const {      // return whether the stack is empty 
        return elems.empty(); 
    } 
}; 

template <typename T, typename CONT> 
void Stack<T,CONT>::push (T const& elem) 
{ 
    elems.push_back(elem);    // append copy of passed elem 
} 

注:上述例子中有两个类模板参数,在定义成员函数的时候,要包含这两个类模板参数“T”和“CONT”

在使用类模板Stack<>的时候,可以只传一个类型参数也可以传递两个类型参数。
如下,:

Stack<int> intStack; 
Stack<double,std::deque<double> >

只传递一个类型参数,第一类型参数就是int,第二个参数就是默认的vector;

传递两个类型参数,第一类型参数就是double,第二个参数是deque;

6、总结

1、所谓类模板,就是包含一个或多个未确定类型的类。
2、使用类模板时必须将具体的类型作为参数传给类模板,编译器实例化该类型的类模板。
3、类模板中只有被调用的成员函数才会被实例化。
4、可以针对特定的类型,对类模板进行特化。
5、可以针对特定的类型,对类模板进行偏特化。
6、可以为类模板的模板参数定义一个默认值,该默认值可以引用前一步定义的模板参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值