繼上篇文章學(xué)習(xí)了如何構(gòu)造容器后, 我們將學(xué)習(xí)如何析構(gòu)容器, 同時實現(xiàn)一些工具函數(shù)用于構(gòu)造與析構(gòu)铸抑。
首先創(chuàng)建一個頭文件"alloc_destroy.h"纱昧, 用來存放下面實現(xiàn)的構(gòu)造與析構(gòu)的工具函數(shù)
1.construct()
template<typename T1, typename T2>
void construct(T1 *p, const T2& value)
{
new (p) T1(value);
}
我們先來活用之前學(xué)過的定位new運算符,實現(xiàn)construt()函數(shù)。construct()在指針p的位置調(diào)用構(gòu)造函數(shù)創(chuàng)建一個對象软舌,我們以后將使用construct()構(gòu)造單個對象。
2.destroy()
我們需要重載好幾個destroy()函數(shù)牛曹,先來看一個最簡單的
template<typename T>
void destroy(T *p)
{
p->~T();
}
這個沒什么好說的佛点,在指針p所指位置調(diào)用析構(gòu)函數(shù)來釋放一個對象。但我們的容器一般都有多個元素黎比,肯定不能一個一個調(diào)用destroy(p)超营,那么我們肯定需要一個能直接析構(gòu)一個區(qū)間的destroy(begin, end):
template<typename ForwardIterator>
void destroy(ForwardIterator begin, ForwardIterator end)
{
aux_destroy(begin, end, std::is_trivially_destructible<decltype(*begin)>());
}
template<typename ForwardIterator>
void aux_destroy(ForwardIterator begin, ForwardIterator end, std::true_type)
{
}
template<typename ForwardIterator>
void aux_destroy(ForwardIterator begin, ForwardIterator end, std::false_type)
{
for (; begin != end; ++begin) {
destroy(&*begin);
}
}
對于沒深入了解過模板的人來說上面的代碼可能有點難以理解,但不要急阅虫,我們一步一步來演闭。
我們的目的是實現(xiàn)函數(shù) destroy(ForwardIterator begin, ForwardIterator end),我們先來看看它的內(nèi)部代碼:
aux_destroy(begin, end, std::is_trivially_destructible<decltype(*begin)>());
它調(diào)用了一個輔助函數(shù)aux_destroy颓帝,同時將區(qū)間參數(shù)向下傳遞米碰。但我們最迷惑的肯定是std::is_trivially_destructible<decltype(*begin)>()
窝革,這的確有點復(fù)雜,我們可以從內(nèi)向外分解它吕座。
decltype(*begin): begin即得到迭代器begin所指的元素虐译, 而decltype()可以得到內(nèi)部元素的類型,合起來就是容器內(nèi)部元素的類型(因為begin的類型與容器內(nèi)其他元素的類型相同)吴趴。
std::is_trivially_destructible<T> : std::is_trivially_destructible是一個類模板漆诽,用來判斷類型T的析構(gòu)函數(shù)是否無關(guān)緊要(trivially)。那什么樣的類的析構(gòu)函數(shù)是trivial的锣枝?:
1. 使用隱式定義的析構(gòu)函數(shù)厢拭,即沒有定義自己析構(gòu)函數(shù)
2. 析構(gòu)函數(shù)不是虛函數(shù)
3. 其基類與非靜態(tài)成員也是可trivially析構(gòu)的
當(dāng)同時滿足上面3個條件時,我們就可稱類型T是 trivially_destructible
說了那么多撇叁, 我們來舉2個例子蚪腐。
class Test
{
private:
int i;
public:
Test(int i) {
this->i = i;
}
};
Test這個類就滿足之前的3個條件,所以它是trivially_destructible税朴。即我們調(diào)不調(diào)用析構(gòu)函數(shù)都無所謂回季,因為它本質(zhì)上并沒有任何需要釋放的資源。就像當(dāng)我們要釋放std::vector<int>類型的容器時正林,我們并不需要在意Int類型元素的釋放泡一,釋放vector<Test>也是一樣的道理。
再來看一個反例:
class Test
{
private:
int i;
int *p;
public:
Test(int i) {
this->i = i;
p = new int;
}
~Test() {
delete p;
}
};
很容易發(fā)現(xiàn)Test類現(xiàn)在多了一個指針變量p,并且p再構(gòu)造函數(shù)被賦值了觅廓。為了保證內(nèi)存不泄漏鼻忠,我們必須手動定義析構(gòu)函數(shù)釋放p。很顯然現(xiàn)在Test類已經(jīng)不是trivially destructible了杈绸,因為我們定義了自己的析構(gòu)函數(shù)帖蔓。此時它的析構(gòu)函數(shù)已經(jīng)不再無關(guān)緊要,因為必須要調(diào)用析構(gòu)函數(shù)來釋放內(nèi)存瞳脓。這個時候釋放std::vector<Test>對象時塑娇, 我們必須要為每個元素析構(gòu),這是庫作者必須要考慮到的事情劫侧。
進(jìn)一步埋酬, 當(dāng)T被判斷是trivially_destructible時, 類型std::is_trivially_destructible<T>將被推導(dǎo)為 std::true_type, 反之則為std::false_type烧栋。
std::is_trivially_destructible<decltype(*begin)>():
現(xiàn)在我們再來看是不是簡單了一點呢写妥? 這段代碼意思即為判斷元素類型是否是trivially_destructible。如果是审姓,類型被轉(zhuǎn)化為std::true_type珍特,反之std::false_type。 最后再調(diào)用構(gòu)造函數(shù)構(gòu)造出std::true_type或std::false_type的對象魔吐。
接下來就簡單了扎筒,編譯器將根據(jù)返回的是std::true_type還是std::false_type調(diào)用不同的輔助函數(shù)aux_destroy():
1. 當(dāng)參數(shù)為std::true_type時
可以看到我們的函數(shù)體為空呼猪,即我們什么都不做,因為類型的析構(gòu)函數(shù)無關(guān)痛癢砸琅,沒有任何影響宋距,只需系統(tǒng)自動回收
2.當(dāng)參數(shù)為std::false_type時
這時我們必須為每個元素調(diào)用構(gòu)造函數(shù)了,為此使用循環(huán)調(diào)用我們之前實現(xiàn)的destroy(T *p)症脂。
template<typename ForwardIterator>
void aux_destroy(ForwardIterator begin, ForwardIterator end, std::false_type)
{
for (; begin != end; ++begin) {
destroy(&*begin);
}
}
終于谚赎,我們實現(xiàn)了想要的destroy(ForwardIterator begin, ForwardIterator end),它能夠幫助我們析構(gòu)[begin, end]范圍的所有元素。
好了诱篷,現(xiàn)在看看我們手上有了哪些工具:
1.
template<typename T1, typename T2>
void construct(T1 *p, const T2& value);
它可以幫助我們在p的位置上調(diào)用構(gòu)造函數(shù)
2.
template<typename T>
void destroy(T *p);
它能夠析構(gòu)p所指的對象
3.
template<typename ForwardIterator>
void destroy(ForwardIterator begin, ForwardIterator end);
它能夠析構(gòu)[begin, end)范圍的所有對象
我們接下來將運用這些函數(shù)壶唤,以及之后要講的分配器一起來實現(xiàn)容器的構(gòu)造與析構(gòu)。
好了棕所,這篇文章也到此結(jié)束啦闸盔。下一篇就真的要開始講Allocator,總算要進(jìn)入正題了琳省,真是不容易迎吵。不過Allocator的難度也很高,應(yīng)該要說很久针贬,還是請做好準(zhǔn)備吧击费。
最后附上本篇文章的代碼地址:https://github.com/Puppas/STL-in-Cpp11/blob/master/STL/alloc_destroy.h
以及github地址:https://github.com/Puppas/STL-in-Cpp11
P.S 謝絕轉(zhuǎn)載,謝謝