如何解决类模板的分离编译问题?
一模板:
模板不是數據類型,只能算是一種行為集合的表示。編譯器在使用模板時,通過更換模板參數來創建數據類型。這個過程就是模板實例化(Instantiation), 從模板類創建得到的類型稱之為特例(specialization),說白了就是創建了一個新類型。 模板實例化取決于編譯器能夠找到可用代碼來創建特例(稱之為實例化要素,point of instantiation),也就是說,編譯器不但要看到模板的聲明,還要看到模板的定義。
1. 為什么類模版不能分離編譯,這要從模板的實例化過程說起:
??? 1)以分離形式寫出的模版類(以tem.h和tem.cpp為例,另外還有主函數main.cpp),在編譯main.cpp時由于只能看到模板聲明而看不到實現,因此不會創建新的類型,但此時不會報錯,因為編譯器認為模板定義在其它文件中,就把問題留給鏈接程序處理。
??? 2)編譯器在編譯tem.cpp時可以解析模板定義并檢查語法,但不能生成成員函數的代碼。因為要生成代碼,需要知道模板參數,即需要一個類型,而不是模板本身。
??? 3)這樣,鏈接程序在main.cpp 或 tem.cpp中都找不到新類型的定義,于是報出無定義成員的錯誤。
另外,實例化是惰性的,只有用到該函數時才會去對模版中的定義進行實例化。
舉一個明顯的例子:
.h文件
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_pRoot(NULL)
{}
~RBTree()
{}
void InOrder()
{
_InOrder(_pRoot);
cout<<endl;
}
void _InOrder(Node* _pRoot);
}
.cpp文件
#include "RBTree.hpp"
template<class K, class V>
void RBTree<K, V>::_InOrder(Node* pRoot)
{
if(pRoot)
{
_InOrder(pRoot->_pLeft);
cout<<pRoot->_key<<" ";
_InOrder(pRoot->_pRight);
}
}
這樣就會產生上述問題:
2. 如何解決這個問題?有三種方式:
??? 1)在實例化要素中讓編譯器看到模板定義。(即寫到一個文件里,聲明和實現放在一起)
??? 2)把類的聲明放在 .hpp 文件,類成員函數定義放在 .cpp 文件然后在測試文件 test.cpp 文件中包含連個頭文件即:.hpp文件和.cpp?文件即可解決問題。
??? 3)使用export關鍵字。
??? 前二種方法通常稱為包含模式,第三種方法則稱為分離模式。第一種方法意味著在使用模板的轉換文件中不但要包含模板聲明文件,還要包含模板定義文件,這樣編譯器就能看到模板的聲明和定義。這樣做的缺點是編譯文件會變得很大,顯然要降低編譯和鏈接速度。第二種方法,通過顯式的模板實例化得到類型,也就是將所有的顯式實例化過程安放在另外的文件中(比如tem_extend.cpp)。這樣新類型不是在main.cpp中產生,而是在tem_extend.cpp中產生,鏈接器就能夠找到它的定義。用這種方法,不會產生巨大的頭文件,加快編譯速度,而且頭文件本身也顯得更加“干凈”和更具有可讀性。但這個方法不能得到惰性實例化的好處,即它將顯式地生成所有的成員函數。第三種方法是在模板定義中使用export關鍵字,剩下的事就讓編譯器去自行處理了。
總結
以上是生活随笔為你收集整理的如何解决类模板的分离编译问题?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sitemesh前段框架基础
- 下一篇: SQL Server无法连接客户端的问题