1. 程式人生 > >實現C++模板類標頭檔案和實現檔案分離的方法

實現C++模板類標頭檔案和實現檔案分離的方法

如何實現C++模板類標頭檔案和實現檔案分離,這個問題和編譯器有關。

引用<<C++primer(第四版)>>裡的觀點:1)標準C++為編譯模板程式碼定義了兩種模型:“包含”模型和“分別編譯”模型。2)所有編譯器都支援“包含”模型,某些編譯器支援“分別編譯”模型。

第一種方法:按C++primer中的“包含”模型,在定義模板類的標頭檔案中的末行用語句:#include "template_compile.cpp"

在類模板標頭檔案template_compile.h中:

[cpp] view plaincopyprint?
  1. template<class T>  
  2. class base  
  3. {  
  4. public:  
  5.     base() {};  
  6.     ~base() {};  
  7.     T add_base(T x,T y);  
  8. };  
  9. #include "template_compile.cpp"  

在類模板的實現檔案template_compile.cpp中:

[cpp] view plaincopyprint?
  1. template<class T>  
  2. T base<T>::add_base(T x,T y)  
  3. {  
  4.     return x+y;  
  5. }  

在使用模板的測試檔案use_template.cpp中:

[cpp] view plaincopyprint?
  1. #include<iostream>  
  2. #include "template_compile.h"  
  3. using namespace std;  
  4. void main()  
  5. {  
  6.     base<int> bobj;  
  7.     cout<<bobj.add_base(2,3)<<endl;  
  8. }  

這種方法不能通過編譯,"template_compile.cpp"檔案不能"看見"“template_compile.h"檔案。

然而:如果我把類模板的實現檔案裡程式碼放在類模板的標頭檔案中,註釋掉:#include "template_compile.cpp",編譯和執行不會有任何錯誤。理論上”把類模板的實現檔案裡程式碼放在類模板的標頭檔案中“和”在定義模板類的標頭檔案中的末行用語句:#include "template_compile.cpp" “是一致的,但編譯器就是通不過。

實驗證明:VC9.0不支援C++primer中所說的“包含”模型。

第二種方法:bruceteen提出的:使用define

在類模板標頭檔案template_compile.h中:

[cpp] view plaincopyprint?
  1. template<class T>  
  2. class base  
  3. {  
  4. public:  
  5.   base() {};  
  6.   ~base() {};  
  7.   T add_base(T x,T y);  
  8. };  
  9. #define FUCK  
  10. #include "template_compile.cpp"  

在類模板的實現檔案template_compile.cpp中:

[c-sharp] view plaincopyprint?
  1. #ifdef FUCK  
  2. template<class T>  
  3. base<T>::add_base(T x,T y)  
  4. {  
  5.   return x+y;  
  6. }  
  7. #endif  

測試檔案不變。

實驗證明:在VC9.0中,這種方法可以實現類模板標頭檔案和實現檔案的分離

方法三:

在類模板標頭檔案template_compile.h中:

[cpp] view plaincopyprint?
  1. template<class T>  
  2. class base  
  3. {  
  4. public:  
  5.     base() {};  
  6.     ~base() {};  
  7.     T add_base(T x,T y);  
  8. };  

在類模板的實現檔案template_compile.cpp中:

[c-sharp] view plaincopyprint?
  1. #include "template_compile.h"  
  2. template<class T>  
  3. base<T>::add_base(T x,T y)  
  4. {  
  5.     return x+y;  
  6. }  

在使用模板的測試檔案use_template.cpp中:使用#include "template_compile.cpp"

[c-sharp] view plaincopyprint?
  1. #include<iostream>  
  2. #include "template_compile.cpp"  
  3. using namespace std;  
  4. void main()  
  5. {  
  6.     base<int> bobj;  
  7.     cout<<bobj.add_base(2,3)<<endl;  
  8. }  

實驗證明:在VC9.0中,這種方法可以實現類模板標頭檔案和實現檔案的分離。

另外實驗證明:VC9.0不支援“分別編譯”模型。


C++中每一個物件所佔用的空間大小,是在編譯的時候就確定的,在模板類沒有真正的被使用之前,編譯器是無法知道,模板類中使用模板型別的物件的所佔用的空間的大小的。只有模板被真正使用的時候,編譯器才知道,模板套用的是什麼型別,應該分配多少空間。這也就是模板類為什麼只是稱之為模板,而不是泛型的緣故。

既然是在編譯的時候,根據套用的不同型別進行編譯,那麼,套用不同型別的模板類實際上就是兩個不同的型別,也就是說,stack<int>和stack<char>是兩個不同的資料型別,他們共同的成員函式也不是同一個函式,只不過具有相似的功能罷了。&amp;amp;lt;img src=&quot;https://pic2.zhimg.com/1772de8abdd112a50e533e3c018535a1_b.jpg&quot; data-rawwidth=&quot;749&quot; data-rawheight=&quot;308&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;749&quot; data-original=&quot;https://pic2.zhimg.com/1772de8abdd112a50e533e3c018535a1_r.jpg&quot;&amp;amp;gt;如上圖所示,很簡短的六行程式碼,用的是STL裡面的stack,stack&amp;amp;amp;lt;int&amp;amp;amp;gt;和stack&amp;amp;amp;lt;char&amp;amp;amp;gt;的預設建構函式和push函式的入口地址是不一樣的,而不同的stack&amp;amp;amp;lt;int&amp;amp;amp;gt;物件相同的函式入口地址是一樣的,這個也反映了模板類在套用不同型別以後,會被編譯出不同程式碼的現象。

如上圖所示,很簡短的六行程式碼,用的是STL裡面的stack,stack<int>和stack<char>的預設建構函式和push函式的入口地址是不一樣的,而不同的stack<int>物件相同的函式入口地址是一樣的,這個也反映了模板類在套用不同型別以後,會被編譯出不同程式碼的現象。

所以模板類的實現,脫離具體的使用,是無法單獨的編譯的;把宣告和實現分開的做法也是不可取的,必須把實現全部寫在標頭檔案裡面。為了清晰,實現可以不寫在class後面的花括號裡面,可以寫在class的外面。