C++將模板實(shí)現(xiàn)放入頭文件原理解析_第1頁(yè)
C++將模板實(shí)現(xiàn)放入頭文件原理解析_第2頁(yè)
C++將模板實(shí)現(xiàn)放入頭文件原理解析_第3頁(yè)
C++將模板實(shí)現(xiàn)放入頭文件原理解析_第4頁(yè)
C++將模板實(shí)現(xiàn)放入頭文件原理解析_第5頁(yè)
已閱讀5頁(yè),還剩3頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第C++將模板實(shí)現(xiàn)放入頭文件原理解析目錄寫(xiě)在前面例子原因分析解決方案方案一方案二參考寫(xiě)在后面

寫(xiě)在前面

本文通過(guò)實(shí)例分析與講解,解釋了為什么C++一般將模板實(shí)現(xiàn)放在頭文件中。這主要與C/C++的編譯機(jī)制以及C++模板的實(shí)現(xiàn)原理相關(guān),詳情見(jiàn)正文。同時(shí),本文給出了不將模板實(shí)現(xiàn)放在頭文件中的解決方案。

例子

現(xiàn)有如下3個(gè)文件:

//add.h

templatetypenameT

TAdd(constTa,constT

//add.cpp

#include"add.h"

templatetypenameT

TAdd(constTa,constTb)

returna+b;

//main.cpp

#include"add.h"

#includeiostream

intmain()

intres=Addint(1,2);

std::coutres"\n";

return0;

}

現(xiàn)象

使用g++-cadd.cpp編譯生成add.o,使用g++-cmain.cpp編譯生成main.o,這兩步都沒(méi)有問(wèn)題。

使用g++-omain.exemain.oadd.o生成main.exe時(shí),報(bào)錯(cuò)undefinedreferencetointAdd(intconst,intconst)。

當(dāng)然,直接g++add.cppmain.cpp-omain.exe肯定也會(huì)報(bào)錯(cuò),這里把編譯和鏈接分開(kāi)是為了更好地展示與分析問(wèn)題。?

原因

出現(xiàn)上述問(wèn)題的原因是:

(1)C/C++源文件是按編譯單元(translationunit)分開(kāi)、獨(dú)立編譯的。所謂translationunit,其實(shí)就是輸入給編譯器的sourcecode,只不過(guò)該sourcecode是經(jīng)過(guò)預(yù)處理(pre-processed?,包括去掉注釋、宏替換、頭文件展開(kāi))的。在本例中,即便你使用g++add.cppmain.cpp-omain.exe,編譯器也是分別編譯add.cpp和main.cpp(注意是預(yù)處理后的)的。在編譯add.cpp時(shí),編譯器根本感知不到main.cpp的存在,反之同理。

(2)C++模板是通過(guò)實(shí)例化(instantiation)來(lái)實(shí)現(xiàn)多態(tài)(polymorphism)的。以函數(shù)模板為例,首先需要區(qū)分函數(shù)模板和模板函數(shù)。本例中,上面代碼的第8~12行是函數(shù)模板,顧名思義,它就是一個(gè)模子,不是具體的函數(shù),是不能運(yùn)行的;當(dāng)用具體的類(lèi)型,如int,實(shí)例化模板參數(shù)T后,會(huì)生成函數(shù)模板的一個(gè)具體實(shí)例,稱(chēng)為模板函數(shù),這是真正可以運(yùn)行的函數(shù)。函數(shù)模板和模板函數(shù)的關(guān)系,可以類(lèi)比類(lèi)和對(duì)象的關(guān)系。以int為例,生成的實(shí)例/模板函數(shù)大概長(zhǎng)這樣(細(xì)節(jié)上肯定和編譯器的實(shí)際實(shí)現(xiàn)有出入,但核心意思不會(huì)變)。

intAdd_int_int(constinta,constintb)

returna+b;

}

?對(duì)于每一個(gè)用到的具體類(lèi)型,編譯器都會(huì)生成對(duì)應(yīng)版本的實(shí)例,當(dāng)函數(shù)調(diào)用時(shí),會(huì)調(diào)用到該實(shí)例。如用到了Addint,就會(huì)生成Add_int_int,用到了Adddouble,就會(huì)生成Add_double_double,等等。本例中,當(dāng)編譯器編譯到第20行,即intres=Addint(1,2);一句時(shí),編譯器就會(huì)試圖生成int版本的模板實(shí)例(即模板函數(shù))。

(3)編譯器為模板生成實(shí)例的必要條件是:1.知道模板的具體定義/實(shí)現(xiàn);2.知道模板參數(shù)對(duì)應(yīng)的實(shí)際類(lèi)型。

分析

下面把上面兩節(jié)內(nèi)容結(jié)合起來(lái)分析。

(1)當(dāng)編譯add.cpp時(shí),相當(dāng)于編譯

templatetypenameT

TAdd(constTa,constT

templatetypenameT

TAdd(constTa,constTb)

returna+b;

}

此時(shí)編譯器雖然知道模板的具體定義,卻不知道模板參數(shù)T的具體類(lèi)型,因此不會(huì)生成任何的實(shí)例化代碼。

(2)當(dāng)編譯main.cpp時(shí),相當(dāng)于編譯

#includeiostream

templatetypenameT

TAdd(constTa,constT

intmain()

intres=Addint(1,2);

std::coutres"\n";

return0;

}

當(dāng)編譯到intres=Addint(1,2);時(shí),編譯器想要生成int版本的函數(shù)實(shí)例,但它找不到函數(shù)模板的具體定義(即Add的函數(shù)體),只好作罷。好在編譯器看到了函數(shù)模板的聲明,于是通過(guò)了編譯,將尋找int版本函數(shù)實(shí)例的任務(wù)留給了鏈接器。?

至此,編譯add.cpp時(shí),只知模板定義,不知模板類(lèi)型參數(shù),無(wú)法生成具體的函數(shù)定義;編譯main.cpp時(shí),只知模板類(lèi)型參數(shù),不知模板定義,同樣無(wú)法生成具體的函數(shù)定義。?

(3)沒(méi)什么好說(shuō)的,鏈接器在add.o和main.o中都沒(méi)找到int版本的Add定義,直接報(bào)錯(cuò)。?

解決方案

方案一

傳統(tǒng)方法:把模板實(shí)現(xiàn)也放在頭文件中。

//add.h

templatetypenameT

TAdd(constTa,constTb)

returna+b;

//main.cpp

#include"add.h"

#includeiostream

intmain()

intres=Addint(1,2);

std::coutres"\n";

return0;

}

當(dāng)編譯main.cpp時(shí),相當(dāng)于編譯?

#includeiostream

templatetypenameT

TAdd(constTa,constTb)

returna+b;

intmain()

intres=Addint(1,2);

std::coutres"\n";

return0;

}

此時(shí)編譯器既知道函數(shù)模板的定義,又知道具體的模板類(lèi)型參數(shù)int,因此可以生成int版本的函數(shù)實(shí)例,不會(huì)出錯(cuò)。?

這種方式的優(yōu)缺點(diǎn)如下:

優(yōu)點(diǎn):可以按需生成。假如我們?cè)趍ain.cpp中調(diào)用了Adddouble(1.0,2.0);,編譯器就會(huì)為我們生成double版本的函數(shù)實(shí)例。缺點(diǎn):不得不把實(shí)現(xiàn)細(xì)節(jié)暴露給用戶(hù)。

方案二

模板聲明和定義分離的方案。?

//add.h

templatetypenameT

TAdd(constTa,constT

//add.cpp

#include"add.h"

templatetypenameT

TAdd(constTa,constTb)

returna+b;

templateintAdd(constinta,constint

//main.cpp

#include"add.h"

#includeiostream

intmain()

intres=Addint(1,2);

std::coutres"\n";

return0;

}

注意,templateintAdd(constinta,constint是函數(shù)模板實(shí)例化(functiontemplateinstantiation)[1],template關(guān)鍵字不能省略,否則,intAdd(constinta,constint會(huì)被編譯器當(dāng)做普通函數(shù)的聲明,從而在鏈接時(shí)又會(huì)報(bào)undefinedreferencetointAdd(intconst,intconst)錯(cuò)誤。?

對(duì)于這種寫(xiě)法,編譯器在編譯add.cpp時(shí),既能看到函數(shù)模板的定義,又能看到具體的模板類(lèi)型參數(shù)int,于是生成了int版本的函數(shù)實(shí)例,整個(gè)程序可以正常編譯運(yùn)行。?

很顯然,這種情況下編譯器只生成了int版本的函數(shù)實(shí)例,所以,在main.cpp中使用Adddouble(1.0,2.0);這樣的代碼肯定是不可以的。這種情況的優(yōu)缺點(diǎn)可以辯證看待:?

優(yōu)點(diǎn):

1.可以隱藏實(shí)現(xiàn)細(xì)節(jié)(我們可以把a(bǔ)dd.cpp做成.lib或.dll);2.也可以限制只實(shí)例化特定的版本。?

缺點(diǎn):就是只能使用特定的幾個(gè)版本,不能像方案一那樣在編譯main.cpp時(shí)根據(jù)具體的調(diào)用情況按需生成。?

從這里也可以看出,模板實(shí)現(xiàn)不一定非得放在頭文件中。

參考

[1]Functiontemplate-

[2]c++-Whycantempla

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論