模板的分离编译
模板不支持分離編譯
我們來分析一下模板為什么不支持分離編譯呢,所謂的分離編譯就是我們在編寫程序的時候可能會出現如下的一種情況就是,(我下面就是舉具體的例子了)
代碼
//*****************template.h***********//
#include<iostream>
using namespace std;template<class T>
class A
{
public:void Show();
private:T a;
};//*****************template.cpp***********//
#include"template.h"template<class T>
void A<T>::Show()
{T m;cout << "hello" << endl;
}//*****************test.cpp***********//
#define _CRT_SECURE_NO_WARNINGS 1
#include"template.h"int main()
{A<int> a;a.Show();return 0;
}
現象一
上面的程序中,我們調用的時候會出現如下的報錯
1>test.obj : error LNK2019: 無法解析的外部符號 "public: void __thiscall A<int>::Show(void)" (?Show@?$A@H@@QAEXXZ),該符號在函數 _main 中被引用
1>W:\Code\C++\TemptlateSeparateCompilation\Debug\TemptlateSeparateCompilation.exe : fatal error LNK1120: 1 個無法解析的外部命令
現象二
我們改一下代碼再來看一個現象,如果我們在主函數中把a.Show給注釋掉,然后在template.cpp把cout<<"hello"<<endl;改寫成cout<<"hello"
在進行編譯的時候,發現竟然沒有報錯,這就奇怪了吧
從預處理,編譯,匯編,鏈接的角度來分析問題
我們知道模板的一個特性,只有當調用的時候才會實例化它,而我們的程序從一開始的文本到最后的可執行程序經歷了四個過程,分別是預處理,編譯,匯編,鏈接,最后是在鏈接的時候出現了錯誤
一.預處理
在預處理的時候,會進行頭文件展開,這個時候會把頭文件中的函數聲明展開到test.cpp和template.cpp中
二.編譯
在編譯test.cpp時由于只能看到模板聲明而看不到實現,因此不會實例化模板函數,但此時不會報錯,因為編譯器認為模板定義在其它文件中,就把問題留給鏈接程序處理。
在編譯template.cpp的時候,編譯的時候會進行一些檢查,比如一些語法的檢查,然后生成匯編代碼,因為我們的模板函數是在調用的時候才會進行實例化,即只有在調用模板函數的時候才會生成代碼,但是在我們的編譯的時候,在template.cpp這個文件中是沒有對函數進行實例化,所以并沒有生成匯編的代碼,這也是上面的第二種情況為什么我們的代碼錯誤從卻沒有被檢查出來的原因,因為我們的根本就沒有生成匯編代碼
三.匯編
再來接著分析,在匯編的時候就是生成二進制代碼,然后是一個.obj文件,在linux下面是.o文件,同時生成一個符號表,符號表中放置的就是我們所有函數的地址。
四.鏈接.
當我們進行最后一步鏈接的時候,執行到a.Show();會去.ob文件中查找我們的函數地址,這里有一個符號表,但是因為我們沒有生成一個函數代碼,所以這個函數表是查找不到的,所以這個時候就會報上面的錯誤。
還需要分析的一個問題就是,為什么我們把函數的聲明和定義放置在一個文件中的時候就可以呢,因為如果我們把函數聲明和定義放置在一個頭文件中的話,我們在編譯的時候,就會頭文件展開了,這個時候調用了我們的函數,然后又是在一個文件內如就會直接直接生成代碼,然后在查找的時候就會很容易的查找到這個內容。
解決辦法
方法一:
使用上面說的內容,放模板函數的聲明和定義放在一個頭文件下面
但是這樣會出現一些問題
(1)放置在頭文件中,暴露了模板函數的 內容
(2)不符合分離編譯的原則
方法二
我們可以在函數聲明的時候加上一個export,就是下面的這種形式
export void Show();
但是很多的編譯器是不支持的
方法三
我們可以顯示的實例化,就是我們可以在頭文件中對我們需要的內容 進行一個顯式的實例化
總結
- 上一篇: “物生皆夭阏”上一句是什么
- 下一篇: 智能指针1.0