C++知識點56——類模板(1、模板類的介紹)
一、類模板
和函式模板一樣,類模板也得通過template關鍵字來宣告和定義,C++標準庫中有很多容器都是類模板
示例
template <typename T> class mystack { public: mystack(); mystack(const mystack<T> &rval); mystack<T> &operator=(const mystack<T> &rval); ~mystack(); void push(const T&ele); void pop(); T top() const; bool empty() const { return elem.empty(); } private: vector<T> elem; }; template <typename T> mystack<T>::mystack() { cout<<__func__<<endl; } template <class T> mystack<T>::mystack(const mystack<T> &rval):elem(rval.elem) { cout<<__func__<<endl; } template <class T> mystack<T> & mystack<T>::operator=(const mystack<T> &rval) { cout<<__func__<<endl; if (this==&rval) { return *this; } this->elem=rval.elem; return *this; } template <typename T> mystack<T>::~mystack() { cout<<__func__<<endl; } template <typename T> void mystack<T>::push(const T &elem) { cout<<__func__<<endl; elem.push_back(elem); } template <typename T> void mystack<T>::pop() { cout<<__func__<<endl; try { elem.pos_back(); } catch (out_of_range){ cout<<"out_of_range"<<endl; } } template <typename T> T mystack<T>::top() const { cout<<__func__<<endl; try { return elem.back(); } catch (out_of_range){ cout<<"out_of_range"<<endl; } }
上述程式碼通過vector定義了一個mystack模板類。模板類的定義依然得通過template和typename關鍵字
template <typename T>
class mystack
{
//......
};
此處的typename可以用class替換
類模板的模板引數可以用來宣告成員變數和成員函式。成員函式可以用在類內實現,也可以在類外實現,和普通類一樣,在類外實現時,要指定作用成員函式的作用域,只不過類的型別時mystack<T>,所以拷貝建構函式和operator=的形參型別都是mystack<T>的引用,作用域也因此是mystack<T>,因為有模板引數,所以,成員函式在類外實現時也要加template關鍵字和模板引數列表(template <typename T>)
二、使用類模板
int main(int argc, char const *argv[])
{
mystack<int> si;
return 0;
}
當例項化一個類模板時,沒有報錯,正常輸出,但是在main函式中新增si.push(10);後,編譯器提示以下錯誤
原因是形參elem沒有push_back操作,形參和成員變數重名了,修改後,輸出結果如下
也就是說,上述程式碼中的類成員函式在呼叫之前,沒報錯,被呼叫後,成員函式報錯了,所以,對於類模板來說,成員函式只有在被呼叫的時候才會例項化,而例項化相關的錯誤只有在例項化時才會出現,在編譯模板本身時並不會出現。類模板的成員函式只有在被呼叫時才會例項化可以節省空間和時間
三、類模板的特化
有時候,通用的類模板對於某些模板實參可能並不適合,這時候就需要對類模板進行特化。特化一個類模板是需要在其起始處新增template<>,然後接下來宣告特化類模板的型別
示例
template<>
class mystack<string>
{
public:
mystack();
mystack(const mystack<string> &rval);
mystack<string> &operator=(const mystack<string> &rval);
~mystack();
void push(const string& ele);
void pop();
string top() const;
bool empty() const {
return elem.empty();
}
private:
deque<string> elem;
};
mystack<string>::mystack()
{
cout<<__func__<<endl;
}
mystack<string>::mystack(const mystack<string> &rval):elem(rval.elem)
{
cout<<__func__<<endl;
}
mystack<string> & mystack<string>::operator=(const mystack<string> &rval)
{
cout<<__func__<<endl;
if (this==&rval) {
return *this;
}
this->elem=rval.elem;
return *this;
}
mystack<string>::~mystack()
{
cout<<__func__<<endl;
}
void mystack<string>::push(const string &ele)
{
cout<<__func__<<endl;
elem.push_back(ele);
}
void mystack<string>::pop()
{
cout<<__func__<<endl;
try {
elem.pop_back();
}
catch (out_of_range &e){
cout<<"out_of_range"<<endl;
}
}
string mystack<string>::top() const
{
cout<<__func__<<endl;
try {
return elem.back();
}
catch (out_of_range &e){
cout<<"out_of_range"<<endl;
}
return "";
}
上述程式碼就是mystack的特化版本,大體上和類模板mystack沒差多少,但是容器型別從vector變為deque,捕獲異常的方式也變成捕獲異常的引用,如果仍然按照類模板的程式碼,會出警告
上述情況說明:1、特化版本的模板類可以和原來的類模板完全不同。2、特化類模板之後,會出現編譯原先類模板時不曾出現的錯誤,所以類模板的特化本質依舊是類模板的例項化
特化只是一種特殊的例項化,但是例項化的方式並不只有特化這一種
參考
《C++ Template》
《C++ Primer》
歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出