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