在完成了STL與泛型編程前四周的學(xué)習(xí)之后扼仲,有一些總結(jié)和心得在這里通過(guò)學(xué)習(xí)筆記的方式分享出來(lái),筆記我是跟著老師在視頻中所講的內(nèi)容按照順序記錄的,也不能說(shuō)是流水賬,對(duì)課程中的一些問(wèn)題還是添加了自己的理解和分析顶燕,供也在學(xué)習(xí)C++的小伙伴用作學(xué)習(xí)交流,如有理解不到位的地方辐真,歡迎批評(píng)指正糕珊。
在STL與泛型編程前幾周的課程中,老師就STL三大部件(迭代器振愿、容器捷犹、算法)的結(jié)構(gòu)與分類(lèi)都做了詳細(xì)的介紹,本周的課程老師介紹了幾種特殊的算法冕末,加深我們對(duì)如何使用算法的認(rèn)識(shí)萍歉。
一.一個(gè)萬(wàn)用的Hash Fuction
我們總是希望hash function產(chǎn)生的hash code越亂越好,盡量不要重復(fù)档桃,因此設(shè)計(jì)hash function有三種形式——設(shè)計(jì)成成員函數(shù)枪孩、設(shè)計(jì)成一般函數(shù)、以struct hash偏特化形式實(shí)現(xiàn)hash function藻肄。
這里首先采用設(shè)計(jì)成成員函數(shù)的形式蔑舞,借由variadic template將不定量的參數(shù)拆解,做到遞歸仅炊。
這種遞歸的方式運(yùn)用了數(shù)學(xué)中黃金比的概念斗幼,即0.618
第三種形式是以struct hash偏特化形式實(shí)現(xiàn)hash function,這種方式并不難理解抚垄,對(duì)不同的類(lèi)型有不同的算法得到hash code
二.Tuple用例
Tuple其實(shí)就是一堆東西組合而成的一種類(lèi)型蜕窿,是各種類(lèi)型的整合谋逻。
聲明:
直接用make_tuple賦值:
還可以用tie將合成類(lèi)型中的成分取出來(lái)分別綁定給不同的變量:
Tuple——元之組合,數(shù)之組合
不斷的繼承自己桐经,用variadic template實(shí)現(xiàn)遞歸毁兆。
三.Type traits
Since C++11,新的標(biāo)準(zhǔn)庫(kù)有幾十個(gè)type traits阴挣,在課程中气堕,老師進(jìn)行了各種type traits測(cè)試,我們寫(xiě)的class畔咧,type traits能回答我們提出的任何問(wèn)題茎芭。將class Foo,class Goo誓沸,class Zoo丟進(jìn)type traits進(jìn)行測(cè)試梅桩,發(fā)現(xiàn)type traits確實(shí)能回答各種問(wèn)題。
這里需要注意的是:&&是move_constructor
測(cè)試之后拜隧,老師還舉了用type traits實(shí)現(xiàn)is_void宿百、is_integral、is_class洪添、is_union垦页、is_pod、is_move_assignable干奢,這里代碼就不一一貼出來(lái)了痊焊。值得注意的是:對(duì)于cout,cout是一個(gè)對(duì)象律胀,自己寫(xiě)的類(lèi)型名要丟給cout則必須進(jìn)行操作符<<重載:
四.Moveable元素對(duì)于各容器速度效能的影響
Moveable對(duì)vector的影響很大宋光,比non-moveable要快很多,無(wú)論是構(gòu)造還是拷貝炭菌,對(duì)于容器vector罪佳,moveable對(duì)其效率的影響都很大,因?yàn)闃?gòu)造vector的本質(zhì)是兩倍成長(zhǎng)空間再搬運(yùn)黑低。
但是對(duì)于容器list赘艳、deque、multiset克握、unordered_multiset蕾管,moveable對(duì)其構(gòu)造效率的影響并不大,對(duì)拷貝的效率還是有巨大的影響:
這里老師寫(xiě)了一個(gè)moveable class用于測(cè)試菩暗,代碼在這里就不貼上來(lái)了掰曾,這里值得注意的是:move版本的實(shí)則是淺拷貝,所以其速度比我們熟悉的常規(guī)copy要快得多停团;而我們經(jīng)常用到的拷貝構(gòu)造是深拷貝旷坦,M C11(C1)中掏熬,C1不是臨時(shí)對(duì)象,耗時(shí)比move版本多得多秒梅;
M C12(std::move(C1))則是淺拷貝旗芬,用這種拷貝方式,必須確保之后不會(huì)再用到C1捆蜀,此時(shí)C1是一個(gè)臨時(shí)對(duì)象疮丛。因此,vector的copy ctor是容器本身的拷貝辆它。
五.Type traits衍生
本周的作業(yè)就是利用traits來(lái)實(shí)現(xiàn)類(lèi)型的篩選誊薄。介紹traits的文章很多,但感覺(jué)大部分文章的說(shuō)明都很晦澀難懂娩井,把一個(gè)并不很復(fù)雜的C++模板的應(yīng)用描述的過(guò)于復(fù)雜暇屋。因此,在這里想把自己的理解跟大家分享一下洞辣,或許我也只是掌握了一點(diǎn)traits的皮毛而已,但也希望這些皮毛能略微抓住你的眼球昙衅,帶給你一些啟發(fā)扬霜。
首先,介紹traits前而涉,回味一下C++的模板及應(yīng)用著瓶,如果你腦海里浮現(xiàn)出的只是為實(shí)現(xiàn)一些函數(shù)或類(lèi)的重用的簡(jiǎn)單模板應(yīng)用,那我要告訴你啼县,你out了材原。最近在整理一些模板的應(yīng)用方式,有時(shí)間的話(huà)會(huì)寫(xiě)出來(lái)分享給大家季眷,本文不會(huì)去詳細(xì)討論traits以外的模板的各種高級(jí)應(yīng)用余蟹。那么,言歸正傳子刮,什么是traits威酒?其實(shí)它并不是一個(gè)新的概念,上個(gè)世紀(jì)90年代中期就已經(jīng)被提出挺峡,只是到了這個(gè)世紀(jì)才在各個(gè)C++庫(kù)中被廣泛使用葵孤,而我也是在這個(gè)概念誕生十多年后才接觸到它。C++之父Bjarne Stroustrup對(duì)traits有如下的描述:
Think of a trait as a small object whose main purpose is to carry informationused by another object or algorithm to determine "policy" or"implementation details".
我不知道官方或一些書(shū)上是如何去解釋traits的橱赠,我的理解是:當(dāng)函數(shù)尤仍,類(lèi)或者一些封裝的通用算法中的某些部分會(huì)因?yàn)閿?shù)據(jù)類(lèi)型不同而導(dǎo)致處理或邏輯不同(而我們又不希望因?yàn)閿?shù)據(jù)類(lèi)型的差異而修改算法本身的封裝時(shí)),traits會(huì)是一種很好的解決方案狭姨。
本以為能很簡(jiǎn)單的描述它宰啦,誰(shuí)知道還是用了如此長(zhǎng)的句子才說(shuō)明清楚鲤嫡,相當(dāng)?shù)膽M愧。大家只要有個(gè)大概的概念就ok了绑莺,甚至即使完全沒(méi)概念也沒(méi)關(guān)系暖眼,下面會(huì)通過(guò)實(shí)際代碼來(lái)說(shuō)明。
先看這樣一個(gè)例子纺裁。如果有一個(gè)模板類(lèi)Test:
[cpp]
template
class Test {
......
};
假設(shè)有這樣的需求诫肠,類(lèi)Test中的某部分處理會(huì)隨著類(lèi)型T的不同而會(huì)有所不同,比如希望判斷T是否為指針類(lèi)型欺缘,當(dāng)T為指針類(lèi)型時(shí)的處理有別于非指針類(lèi)型栋豫,怎么做?
模板里再加個(gè)參數(shù)谚殊,如下
[cpp]
template
class Test {
......// can use isPointer to judge whether T is apointer
};
然后用戶(hù)通過(guò)多傳一個(gè)模板類(lèi)型來(lái)告訴Test類(lèi)當(dāng)前T是否為指針丧鸯。(Test)
很抱歉,所有的正常點(diǎn)的用戶(hù)都會(huì)抱怨這樣的封裝嫩絮,因?yàn)橛脩?hù)不理解為什么要讓他們?nèi)リP(guān)心自己的模板類(lèi)型是否為指針丛肢,既然是Test類(lèi)本身的邏輯,為什么麻煩用戶(hù)呢剿干?
由于我們很難去限制用戶(hù)在使用模板類(lèi)時(shí)是使用指針還是基本數(shù)據(jù)類(lèi)型還是自定義類(lèi)型蜂怎,而用常規(guī)方法也沒(méi)有很好的方法去判斷當(dāng)前的T的類(lèi)型。traits怎么做呢置尔?
定義traits結(jié)構(gòu):
[cpp]
template
struct TraitsHelper {
static const bool isPointer = false;
};
template
struct TraitsHelper {
static const bool isPointer = true;
};
也許你會(huì)很困惑杠步,結(jié)構(gòu)體里就一個(gè)靜態(tài)常量,沒(méi)有任何方法和成員變量榜轿,有什么用呢幽歼?解釋一下,第一個(gè)結(jié)構(gòu)體的功能是定義所有TraitsHelper中isPointer的默認(rèn)值都是false谬盐,而第二個(gè)結(jié)構(gòu)體的功能是當(dāng)模板類(lèi)型T為指針時(shí)甸私,isPointer的值為true。也就是說(shuō)我們可以如下來(lái)判斷當(dāng)前類(lèi)型:
TraitsHelper::isPointer值為false设褐,可以得出當(dāng)前類(lèi)型int非指針類(lèi)型
TraitsHelper::isPointer值為true颠蕴,可以得出當(dāng)前類(lèi)型int*為指針類(lèi)型
也許看到這里部分人會(huì)認(rèn)為我簡(jiǎn)直是在說(shuō)廢話(huà),請(qǐng)?jiān)僮约浩肺断轮觯@樣是否就可以在上面Test類(lèi)的定義中直接使用TraitsHelper::isPointer來(lái)判斷當(dāng)前T的類(lèi)型了犀被。
[cpp]
if (TraitsHelper::isPointer)
......
else
......
再看第二個(gè)例子:
還是一個(gè)模板類(lèi)Test:
[cpp]
template
class Test {
public:
int Compute(int d);
private:
T mData;
};
它有一個(gè)Compute方法來(lái)做一些計(jì)算,具有int型的參數(shù)并返回int型的值外冀。
現(xiàn)在需求變了寡键,需要在T為int類(lèi)型時(shí),Compute方法的參數(shù)為int雪隧,返回類(lèi)型也為int西轩,當(dāng)T為float時(shí)员舵,Compute方法的參數(shù)為float,返回類(lèi)型為int藕畔,而當(dāng)T為其他類(lèi)型马僻,Compute方法的參數(shù)為T(mén),返回類(lèi)型也為T(mén)注服,怎么做呢韭邓?還是用traits的方式思考下。
[cpp]
template
struct TraitsHelper {
typedef T ret_type;
typedef T par_type;
};
template <>
struct TraitsHelper {
typedef int ret_type;
typedef int par_type;
};
template <>
struct TraitsHelper {
typedef float ret_type;
typedef int par_type;
};
然后我們?cè)侔裈est類(lèi)也更新下:
[cpp]
template
class Test {
public:
TraitsHelper::ret_typeCompute(TraitsHelper::par_type d);
private:
T mData;
};
可見(jiàn)溶弟,我們把因類(lèi)型不同而引起的變化隔離在了Test類(lèi)以外女淑,對(duì)用戶(hù)而言完全不需要去關(guān)心這些邏輯,他們甚至不需要知道我們是否使用了traits來(lái)解決了這個(gè)問(wèn)題辜御。到這里鸭你,再讓我們回過(guò)來(lái)取品味下開(kāi)始我說(shuō)的那句話(huà):當(dāng)函數(shù),類(lèi)或者一些封裝的通用算法中的某些部分會(huì)因?yàn)閿?shù)據(jù)類(lèi)型不同而導(dǎo)致處理或邏輯不同時(shí)擒权,traits會(huì)是一種很好的解決方案袱巨。最后,讓我們記住它吧菜拓,traits瓣窄,一種模板的應(yīng)用,非常非常有用的東東纳鼎。