c++ 模板
模板提供了一個用途廣泛且強大的能力,即在編譯時生成代碼。它們對生成大量形式相似但只類型不同的代碼尤其有用。
模板一般使用會在頭文件聲明和定義,聲明和定義放在一起,不夠好。真正使用采取以下兩種方式。
一、隱式實例化
??? 如果想允許用戶用他們自己的類型去實例化類模板,那么就需要使用隱式模板實例化。例如,假設你提供一個智能指針類模板smart_pointer<T>,那么你就不能預知用戶會用什么類型去實例化它。但是,編譯器需要在使用它時訪問到此模板定義。這意味若必須在頭文件中暴露模板定義。這是隱式實例化方法在程序設計健壯性方面的最大缺點。不過在這種情況下,即使不能隱藏實現細節,也應當努力去隔離它們。
? 假設你需要在頭文件中包含模板定義,輕松而誘人的做法是在類定義中直接內聯模板定義。這是一種我已經歸類為拙劣設計的做法,而這在模板中也不例外。我的建議是,將所有的模板實現細節包含在單獨的實現頭文件中,該頭文件被主要公有頭文件包含。以Stack類模板為例,可以提供如下主要公有頭文件:
??
#ifndef STACK_H #define STACK_H #include<vector>template<typename T> class Stack { public://Stack();void Push(T val);T Pop();bool IsEmpty()const;private:std::vector<T> mStack; }; //使用單獨的頭文件隔離所有實現細節 #include "stack_priv.h"#endif // STACK_H?
#ifndef STACK_PRIV_H #define STACK_PRIV_Htemplate<typename T> void Stack<T>::Push(T val) {mStack.push_back(val); }template<typename T> T Stack<T>::Pop() {if(IsEmpty()){return T();}T val=mStack.back();mStack.pop_back();return val; }template<typename T> bool Stack<T>::IsEmpty()const {return mStack.empty(); } #endif // STACK_PRIV_H應用:
?
#include "stack.h" void MainWindow::on_pushButton_clicked() {Stack<int> intStack;qDebug()<<"Empty:"<<intStack.IsEmpty();intStack.Push(10);qDebug()<<"Empty:"<<intStack.IsEmpty();int val=intStack.Pop();qDebug()<<"Popped off:"<<val;qDebug()<<"Empty:"<<intStack.IsEmpty();}很多基于模板的高質量API使用了這種技巧,比如Boost的各種頭文件。它具有保持主要公有頭文件整潔的好處,其方法是在實現細節的同時,將必須暴露的內部細節隔離到單獨的頭文件中,這個頭文件被明確指定為包含私有細節。(同樣的技巧可以用于將有意內聯的函數細節與其聲明相隔離。)
把模板定義包含在頭文件中的技巧稱為包含模型。值得注意的是,有一個可供選擇的方法稱作分離模型,它允許在.h文件中以export關鍵字開頭來聲明類模板,這樣模板函數的實現就可以出現在.cpp文件中了。站在API設計的角度,這是一種更可取的模式,因為它允許我們從公有頭文件中移除所有實現細節。大部分編譯器都不支持這個功能。(備注:C++11標準中已經廢除此特性,export關鍵字保留待用。)
?
二、顯式實列化????
如果只想為API提供一部分預設的模板特化集合,而不允許用戶創建其他模板特化,那么可以選擇完全隱藏私有代碼。比如,如果已經創建了3D向量類模板Vector3D<T>,你也許只想提供int、short、float及double這些類型的模板特化,并且不想讓使用者創建其他特化。
這種情況下,可以把模板定義放進.cpp文件,并使用顯式模板實例化那些你需要作為API的一部分導出的特化。template關鍵字可以用來創建顯式實例化。比如上面給出的Stack模板為例,可以使用下而的語句為int類型創建顯式實例化: template class Stack<int>;
這會促使編譯器在代碼的這個位置為特化int類型生成代碼。這樣一來,編譯器以后不會嘗試在代碼的其他位置隱式實例化這種特化,所以使用顯式實例化可以減少構建時間。
?
#ifndef STACK_H #define STACK_H #include<vector>template<typename T> class Stack { public:Stack();void Push(T val);T Pop();bool IsEmpty()const;private:std::vector<T> mStack; };#endif // STACK_H #include "stack.h" template<typename T> Stack<T>::Stack() {} template<typename T> void Stack<T>::Push(T val) {mStack.push_back(val); }template<typename T> T Stack<T>::Pop() {if(IsEmpty()){return T();}T val=mStack.back();mStack.pop_back();return val; }template<typename T> bool Stack<T>::IsEmpty()const {return mStack.empty(); }// explicit template instantiation //顯式模板實例化 template class Stack<int>; template class Stack<double>; template class Stack<std::string>;?
應用
#include "stack.h"typedef Stack<double> DoubleStack; void MainWindow::on_pushButton_clicked() {Stack<int> intStack;qDebug()<<"Empty:"<<intStack.IsEmpty();intStack.Push(10);qDebug()<<"Empty:"<<intStack.IsEmpty();int val=intStack.Pop();qDebug()<<"Popped off:"<<val;qDebug()<<"Empty:"<<intStack.IsEmpty();DoubleStack doubleStack;qDebug()<<"Empty:"<<doubleStack.IsEmpty();doubleStack.Push(9.91);qDebug()<<"Empty:"<<intStack.IsEmpty();int dval=doubleStack.Pop();qDebug()<<"Popped off:"<<dval;qDebug()<<"Empty:"<<doubleStack.IsEmpty();}//顯式模板實例化
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
這里重要的是最后三行,它們為int、double及std::string類型創建Stack類模板的顯式實例化。用戶不能創建其他特化(編譯器也不能為用戶創建隱式實例化),因為
實現細節隱藏在.cpp文件中。總之,實現細節確實成功地隱藏在.cpp文件中了。
為了向用戶指出可以使用哪些模板特化(即那些已經顯式實例化的模板特化),可以在公有頭文件的末尾添加一些類型定義(typedef),比如:
typedef Stack<int> IntStack;
typedef Stack<double> DoubleStack;
typedef Stack<std::string> StringStack;
值得注意的是,采用這種模板風格不僅使你的代碼構建由于移除了隱式實例化而變得更快,而且通過從頭文件中移除模板定義,降低了#include與API的耦合度,同時減少了客戶程序因為包含API頭文件而必須每次編譯的額外代碼.
如果只需要一些確定的特化集合,那么盡量選擇顯式模板實例化。這樣做刻意隱藏私有細節并降低構建時間。
?
轉載于:https://www.cnblogs.com/ike_li/p/8761415.html
總結
- 上一篇: easyui datebox不可编辑设置
- 下一篇: stylus-loader (copy