C++ 模板詳解(二):類模板的概念和基本使用方式

與函數模板相似地(C++模板詳解(一):函數模板的概念和特性) ,類也能夠被一種或多種類型參數化。例如,容器類就是一個具備這種特性的典型例子,它一般被用於管理某種特定類型的元素。只要使用類模板,咱們就能夠實現容器類,而不須要肯定容器中元素的類型。html

1、類模板的實現

在這篇博文中,咱們使用Stack做爲類模板的例子。函數

(1.1) 類模板的聲明

#include <vector>
#include <deque>
#include <stdexcept>

template<typename T>
class Stack
{
    std::vector<T> elems;   // 存儲元素的容器。

public:
    void push(const T& value);  // 將元素壓入棧中。
    void pop();                 // 將棧頂元素彈出棧。
    T top() const;              // 查看棧頂元素的副本。
    bool empty() const {        // 檢查棧是否爲空。
        return elems.empty();
    }
};

template<typename T>
void Stack<T>::push(const T& value)
{
    elems.push_back(value);
}

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

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

如上所示,類模板的聲明和函數模板的聲明很類似:在聲明以前,咱們先聲明參數類型的標識符code

template<typename /*class*/ T>
class Stack
{
    //...  
};

固然,也可使用關鍵字class來代替typename。在類模板的內部,類型T能夠像其它的類型同樣,用於聲明成員變量和成員函數。在這個例子中,類的類型是Stack<T>,其中T是模板參數。所以,當在聲明中須要使用該類的類型時,咱們必需要使用Stack<T>。例如,若是要聲明本身實現的拷貝構造函數和賦值運算符,那就應該這樣來編寫:htm

template<typename T>
class Stack
{
    //...
    Stack(const Stack<T>& other);               // 這裏的構造函數名稱須要與類名相同(Stack)
    Stack<T>& operator=(const Stack<T>& other); // 這裏的拷貝構造函數須要使用類的類型(Stack<T>)
    //...
};

然而, 當須要使用類名而不是類的類型時,就應該只用Stack。例如,當指定類的名稱,或是須要編寫構造函數、析構函數時,就須要使用Stackblog

(1.2) 類模板的實現

爲了定義類模板的成員函數,咱們必需要指定該成員函數是一個函數模板(使用template<typename T>),並且還須要使用這個類模板的完整類型限定運算符Stack<T>::。所以,成員函數push的完整定義以下:get

template<typename T>
void Stack<T>::push(const T& value)
{
    elems.push_back(value);
}

其它成員函數的實現也是相似的;和普通類定義相同,徹底也能夠將成員函數的實現內聯地寫在類中,例如:string

template<typename T>
class Stack
{
    std::vector<T> elems;   // 存儲元素的容器。
    
public:
    // ...
    bool empty() const {        // 檢查棧是否爲空。
        return elems.empty();
    }
};

2、類模板的使用

參見以下的main函數代碼:io

int main()
{
    try 
    {
        Stack<int> intStack;
        Stack<std::string> stringStack;

        // 使用int棧
        intStack.push(7);
        std::cout << intStack.top() << std::endl;

        // 使用string棧
        stringStack.push("hello");
        std::cout << stringStack.top() << std::endl;

        stringStack.pop();
        stringStack.pop();
    }
    catch (std::exception &ex)
    {
        std::cerr << "Exception: " << ex.what() << std::endl;
        return EXIT_FAILURE;
    }
    return 0;
}

注意:只有那些被調用了的成員函數,纔會產生這些函數的實例化代碼。模板

因此,針對這個類模板,缺省的構造函數、pushtop方法都針對intstd::string進行了實例化。然而, pop方法只提供了std::string的實例化。這樣作的好處是:class

  • 能夠節省時間和空間。
  • 對於那些未能提供全部成員函數中全部操做的類型,也可使用該類型來實例化類模板。

另外一方面,若是類中含有靜態成員,那麼用來實例化的每種類型,都會實例化這些靜態成員。

相關文章
相關標籤/搜索