2 引入模板,支持復(fù)雜類型
本項(xiàng)目GitHub: HuangCheng72/HCSTL: 我的STL實(shí)現(xiàn) (github.com): https://github.com/HuangCheng72/HCSTL
進(jìn)入正文。
我們?cè)谏弦黄呀?jīng)實(shí)現(xiàn)了只支持double類型的vector,那么問題來了,如果我們要實(shí)現(xiàn)支持C++全部?jī)?nèi)建類型的vector,該怎么做?我們可以考慮全都重新實(shí)現(xiàn)一遍,但這樣工作量顯然是太大了。所以我們需要C++的模板機(jī)制,把vector變成一個(gè)模板類。內(nèi)建類型,很好實(shí)現(xiàn),直接加上模板語(yǔ)句 template 開文本替換用 T (其實(shí)你自己可以指定任意名字,Tp,E什么的都行)全部替換一下double就行了:
但是問題來了,如果不是內(nèi)建類型的數(shù)據(jù)呢?比如用戶自己定義的類的對(duì)象這種數(shù)據(jù),它和內(nèi)建類型的數(shù)據(jù)有什么區(qū)別?
大家都上過C++課,也都應(yīng)該知道,類的對(duì)象是new出來的,而內(nèi)建類型的數(shù)據(jù)是可以不用new的(要是非要new那我也沒話說)。
而new一個(gè)類的對(duì)象,涉及了兩個(gè)操作:
申請(qǐng)一塊內(nèi)存空間,用來存放這個(gè)對(duì)象。
在這塊空間上根據(jù)參數(shù)輸入調(diào)用類相應(yīng)的構(gòu)造函數(shù),創(chuàng)建這個(gè)對(duì)象(初始化內(nèi)存空間的各個(gè)參數(shù))。
在我們上面的代碼中,我們可以看到,在vector的構(gòu)造函數(shù)和輔助函數(shù)中,我們都已經(jīng)申請(qǐng)了一段內(nèi)存空間(數(shù)組)用來存放vector中的數(shù)據(jù)。我們可以有兩種存放對(duì)象的方法:
將vector的數(shù)組所存放數(shù)據(jù)的類型改為T類型的指針,然后每個(gè)元素都是一個(gè)對(duì)象的指針,對(duì)象直接new出來。
將對(duì)象在vector的數(shù)組空間上創(chuàng)建。
為了與其他類型相統(tǒng)一,我們采用第二種方法,第一種方法如果感興趣的話可以自行嘗試,本教程對(duì)此不討論。
針對(duì)第二種方法,C++有一種運(yùn)算符,placement new(定位new),它的作用是在指定的內(nèi)存空間上創(chuàng)建對(duì)象,用法如下:
我們可以考慮采用定位new,來實(shí)現(xiàn)我們的方法,將對(duì)象在vector的數(shù)組空間上創(chuàng)建。
以帶參構(gòu)造函數(shù)為例:
那么就引出一個(gè)問題,我們?cè)撛趺磁袛嗄兀?/p>
我們自然而然可以想到,能不能實(shí)現(xiàn)一個(gè)判斷的函數(shù),這個(gè)函數(shù)輸入的參數(shù)是一種類型,返回一個(gè)bool值,如果輸入的參數(shù)是C++內(nèi)建類型,則返回true,如果輸入的參數(shù)不是C++內(nèi)建類型,則返回false,這樣是不是能夠解決問題了?
但是很遺憾,C++的函數(shù)參數(shù)不能是一種類型。當(dāng)然,如果你要將類型名字轉(zhuǎn)化為字符串,然后用字符串進(jìn)行判斷也是可以的,不過本教程對(duì)此不討論。
STL 對(duì)此采取的方法是類型萃取(type_traits),它的意義就是將類型的信息提取出來,可以通過一個(gè)判斷機(jī)制進(jìn)行判斷,達(dá)到我們的目的。類型萃取的實(shí)現(xiàn)采用了模板特化的思路。
在這里需要介紹一下POD類型:
POD(Plain Old Data)類型,指的是C++的內(nèi)建數(shù)據(jù)類型,還有原生指針和C風(fēng)格的結(jié)構(gòu)體聯(lián)合體等。標(biāo)準(zhǔn)的定義是:能用C的memcpy()等函數(shù)進(jìn)行操作 的類、結(jié)構(gòu)體或聯(lián)合體。POD類型有以下標(biāo)準(zhǔn):
沒有用戶自定義的構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝賦值運(yùn)算符;
沒有虛函數(shù)和虛基類;
所有非靜態(tài)成員都是public;
所有非靜態(tài)成員都是POD類型;
沒有繼承或只繼承了POD類型。
POD類型和復(fù)雜數(shù)據(jù)類型最大的區(qū)別是四個(gè)方面:構(gòu)造(默認(rèn)構(gòu)造器)、析構(gòu)、賦值、復(fù)制(拷貝構(gòu)造器),即default_constructor,destructor,assignment_operator,copy_constructor。 復(fù)雜數(shù)據(jù)類型先天缺少這四樣(編譯器給它加上的不算),而可以認(rèn)為POD類型是先天具備或者 根本不用考慮 這四樣。
請(qǐng)新建一個(gè)頭文件為 type_traits.h ,在這個(gè)文件中實(shí)現(xiàn)類型萃取。
首先用兩個(gè)空結(jié)構(gòu)體表示true和false的結(jié)果(不用bool類型的原因是因?yàn)榭战Y(jié)構(gòu)體不占用內(nèi)存空間,bool變量還占用1字節(jié)空間):
然后利用C++的模板參數(shù)特化,將這兩個(gè)結(jié)構(gòu)體轉(zhuǎn)化為bool值可以用于判斷。
這是類型萃取的模板類:
這是針對(duì)內(nèi)建類型的特化實(shí)現(xiàn):
類型萃取這部分使用結(jié)構(gòu)體模板實(shí)現(xiàn),最大的好處是空模板不占用內(nèi)存空間(如果使用bool變量必然要占用至少一個(gè)字節(jié)的內(nèi)存空間),同時(shí)結(jié)構(gòu)體類型還可以作為模板參數(shù)傳遞,還有就是C風(fēng)格結(jié)構(gòu)體也是POD,這樣就不用管構(gòu)造和析構(gòu)的事情了。
所以,通過類型萃取,我們就可以寫我們的判斷條件了,還是以之前的帶參構(gòu)造函數(shù)為例,可以寫成:
因此,我們可以實(shí)現(xiàn)支持POD和非POD(non-POD)類型數(shù)據(jù)的vector了。
修改main.cpp,嘗試運(yùn)行一下non-POD類型的簡(jiǎn)單測(cè)試:
但是我們會(huì)發(fā)現(xiàn)運(yùn)行不了。我這里的IDE報(bào)錯(cuò)為:
通過查詢,該錯(cuò)誤代碼 0xC0000374 的含義為 A heap has been corrupted ,即堆內(nèi)存損壞。
debug發(fā)現(xiàn),問題出在輔助函數(shù)這里。
這是怎么回事呢?
分析代碼上下文我們發(fā)現(xiàn)了一個(gè)問題。
這就出現(xiàn)了歧義,編譯器不知道你要銷毀的是到底是哪個(gè),貿(mào)然銷毀導(dǎo)致堆內(nèi)存損壞。這個(gè)問題在析構(gòu)函數(shù)中也是一樣存在的。
但是我們暫時(shí)沒有好的解決方法,所以先擱置這個(gè)問題,加一個(gè)判斷,先run起來再說
run起來,可以看到main.cpp中我們的簡(jiǎn)單測(cè)試順利過關(guān)。
歡迎訪問本項(xiàng)目的GitHub倉(cāng)庫(kù),如果對(duì)您有幫助,麻煩給項(xiàng)目一個(gè)star,謝謝!
HuangCheng72/HCSTL: 我的STL實(shí)現(xiàn) (github.com): https://github.com/HuangCheng72/HCSTL