網(wǎng)站必須做ssl認(rèn)證淘寶店鋪推廣方式有哪些
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 創(chuàng)作不易,感謝三連支持?
一、非類型模版參數(shù)
模板參數(shù)分類為類型形參與非類型形參。
類型形參即:出現(xiàn)在模板參數(shù)列表中,跟在class或者typename之類的參數(shù)類型名稱。
非類型形參,就是用一個(gè)常量作為類(函數(shù))模板的一個(gè)參數(shù),在類(函數(shù))模板中可將該參數(shù)當(dāng)成常量來使用。
注意:
非類型的模板參數(shù)必須在編譯期就能確認(rèn)結(jié)果。(分離編譯會講解)
?
我們來介紹一個(gè)c++11引入的array
? ? ? ?array的底層其實(shí)封裝的是一個(gè)靜態(tài)數(shù)組。并且用到了非類型形參,在這里指代的是底層靜態(tài)數(shù)組的容量大小。
思考:
1、為什么要有這個(gè)非模版形參??define定義宏常量難道不香嗎??
? ? ??define定義宏常量有時(shí)也可以解決問題,但是宏常量的作用域是全局,比如我們想讓一個(gè)數(shù)組是10的容量,一個(gè)數(shù)組是20的容量,顯然是做不到的,但是模版是可以做到的!!我們不傳的時(shí)候N就是缺省值,傳的時(shí)候就是我們指定的容量。
?2、我直接用靜態(tài)數(shù)組不行嗎?為什么非得用類把他封起來??
(1)為了增加檢查的功能,我們知道vs對于靜態(tài)數(shù)組的越界功能是抽查行為,因此不是很安全,而我們把他封裝在類里面,這樣就可以去寫個(gè)斷言來防止數(shù)組越界
?(2)[ ]的重載不僅可以增加檢查功能,還可以去控制靜態(tài)數(shù)組內(nèi)容是否可以被寫,同時(shí)還可以利用這個(gè)類去增加許多新的接口
?3.能夠作為非類型模版參數(shù)的有哪些類型??
? ? ? 只能是和int相似類型的才行,比如char、short、int、long int ……浮點(diǎn)數(shù)類對象以及字符串是不允許作為非類型模板參數(shù)的。
二、模版的特化
? ? ? ? ?通常情況下,使用模板可以實(shí)現(xiàn)一些與類型無關(guān)的代碼,但對于一些特殊類型的可能會得到一些錯(cuò)誤的結(jié)果,需要特殊處理,比如:實(shí)現(xiàn)了一個(gè)專門用來進(jìn)行小于比較的函數(shù)模板
? ? ? ? 可以看到,Less絕對多數(shù)情況下都可以正常比較,但是在特殊場景下就得到錯(cuò)誤的結(jié)果。上述示例中,p1指向的d1顯然大于p2指向的d2對象,但是Less內(nèi)部并沒有比較p1和p2指向的對象內(nèi)容,而比較的是p1和p2指針的地址,這就無法達(dá)到預(yù)期而錯(cuò)誤。
? ? ? ? ?此時(shí),就需要對模板進(jìn)行特化。即:在原模板類的基礎(chǔ)上,針對特殊類型所進(jìn)行特殊化的實(shí)現(xiàn)方式。模板特化中分為函數(shù)模板特化與類模板特化。
?
2.1 函數(shù)模版特化
函數(shù)模板的特化步驟:
1. 必須要先有一個(gè)基礎(chǔ)的函數(shù)模板
2. 關(guān)鍵字template后面接一對空的尖括號<>
3. 函數(shù)名后跟一對尖括號,尖括號中指定需要特化的類型
4. 函數(shù)形參表: 必須要和模板函數(shù)的基礎(chǔ)參數(shù)類型完全相同,如果不同編譯器可能會報(bào)一些奇怪的錯(cuò)誤。
我們展示一下用法:
相當(dāng)于是我們特殊化了一個(gè)版本出來,這個(gè)版本可以去比較指針解引用的內(nèi)容!
但是我們還有這樣一個(gè)方法——利用函數(shù)匹配的規(guī)則,直接把這個(gè)特殊類型的函數(shù)給出來
? ? ? ? ?我們可以把函數(shù)模版當(dāng)成是冰箱里的菜,模版特化的函數(shù)當(dāng)成是預(yù)制菜,最后這個(gè)簡單函數(shù)是現(xiàn)成菜。當(dāng)我們有現(xiàn)成的吃的時(shí)候,就不會考慮去吃沒做過的菜。這其實(shí)就是函數(shù)匹配規(guī)則!
? ? ? ?并且這種函數(shù)實(shí)現(xiàn)簡單明了,代碼的可讀性高,容易書寫,因?yàn)閷τ谝恍﹨?shù)類型復(fù)雜的函數(shù)模板,特化時(shí)特別給出,因此函數(shù)模板不建議特化。
?2.2 類模版特化
? ? ? ?函數(shù)有匹配規(guī)則,所以其實(shí)不怎么依賴特化,但是類并沒有匹配規(guī)則啊!!所以特化最廣泛的使用是在類中。類模版特化的步驟和函數(shù)模版特化的步驟是相似的。
2.2.1 全特化
全特化即是將模板參數(shù)列表中所有的參數(shù)都確定化
2.2.2 部分特化
部分特化的意思就是說,我們把其中一部分參數(shù)進(jìn)行特化了。
2.2.3 偏特化(非常重要)
? ? ? ?偏特化并不僅僅是指特化部分參數(shù),而是針對模板參數(shù)更進(jìn)一步的條件限制所設(shè)計(jì)出來的一個(gè)特化版本。
? ? ? 比如偏特化為指針類型(比較常見),或者是偏特化為引用類型(不常見)。
? ? ? 后面講到仿函數(shù)的時(shí)候會做更進(jìn)一步的介紹
三、模版分離編譯
? ? ? ? 一個(gè)程序(項(xiàng)目)由若干個(gè)源文件共同實(shí)現(xiàn),而每個(gè)源文件單獨(dú)編譯生成目標(biāo)文件,最后將所有目標(biāo)文件鏈接起來形成單一的可執(zhí)行文件的過程稱為分離編譯模式。
?
? ? ? 一般來說,在我們書寫大項(xiàng)工程的時(shí)候,為了保證代碼的簡潔性,我們常常將函數(shù)聲明放在一個(gè)頭文件里,將函數(shù)定義放在一個(gè)源文件里,然后再用另外一個(gè)源文件去進(jìn)行測試。但是在模版這個(gè)地方,分離編譯成為了一個(gè)難題!
3.1 模版的分離編譯
假如有以下場景,模板的聲明與定義分離開,在頭文件中進(jìn)行聲明,源文件中完成定義:
// flby.h
template<class T>
T Add(const T& left, const T& right);
// flby.cpp
#include"flby.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// test.cpp
#include"flby.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
為什么會這樣呢??
3.2 解決方法
方法一:將聲明和定義放到一個(gè)文件 "xxx.hpp" 里面或者xxx.h其實(shí)也是可以的。
? ? ? 因?yàn)樽詈蠖紩跍y試文件里面展開,這樣編譯的過程就可以進(jìn)行實(shí)例化生成函數(shù)。一般比較推薦使用這種。
方法二:模板定義的位置顯式實(shí)例化。這種方法不實(shí)用,不推薦使用。
顯式實(shí)例化的意思就是,你不是推斷不出來嗎??那我就直接告訴你要生成什么樣的函數(shù)!
四、模版的總結(jié)
優(yōu)點(diǎn):
1. 模板復(fù)用了代碼,節(jié)省資源,更快的迭代開發(fā),C++的標(biāo)準(zhǔn)模板庫(STL)因此而產(chǎn)生
2. 增強(qiáng)了代碼的靈活性
缺陷:
1. 模板會導(dǎo)致代碼膨脹問題,也會導(dǎo)致編譯時(shí)間變長(需要推導(dǎo)并生成實(shí)例化函數(shù))
2. 出現(xiàn)模板編譯錯(cuò)誤時(shí),錯(cuò)誤信息非常凌亂,不易定位錯(cuò)誤
五、priority_queue的介紹
priority_queue的文檔介紹
1. 優(yōu)先隊(duì)列是一種容器適配器,根據(jù)嚴(yán)格的弱排序標(biāo)準(zhǔn),它的第一個(gè)元素總是它所包含的元素中最大(小)的。
2. 此上下文類似于堆,在堆中可以隨時(shí)插入元素,并且只能檢索最大堆元素(優(yōu)先隊(duì)列中位于頂部的元素)。
3. 優(yōu)先隊(duì)列被實(shí)現(xiàn)為容器適配器,容器適配器即將特定容器類封裝作為其底層容器類,queue提供一組特定的成員函數(shù)來訪問其元素。元素從特定容器的“尾部”彈出,其稱為優(yōu)先隊(duì)列的頂部。
4. 底層容器可以是任何標(biāo)準(zhǔn)容器類模板,也可以是其他特定設(shè)計(jì)的容器類。容器應(yīng)該可以通過隨機(jī)訪問迭代器訪問,并支持以下操作:
?
empty():檢測容器是否為空
size():返回容器中有效元素個(gè)數(shù)
front():返回容器中第一個(gè)元素的引用
push_back():在容器尾部插入元素
pop_back():刪除容器尾部元素
5. 標(biāo)準(zhǔn)容器類vector和deque滿足這些需求。默認(rèn)情況下,如果沒有為特定的priority_queue類實(shí)例化指定容器類,則使用vector。
6. 需要支持隨機(jī)訪問迭代器,以便始終在內(nèi)部保持堆結(jié)構(gòu)。容器適配器通過在需要時(shí)自動調(diào)用算法函數(shù)make_heap、push_heap和pop_heap來自動完成此操作。
?
其實(shí)優(yōu)先級隊(duì)列就是我們數(shù)據(jù)結(jié)構(gòu)里的堆!!
DS:二叉樹的順序結(jié)構(gòu)及堆的實(shí)現(xiàn)_順序打印堆-CSDN博客
大家可以看看博主的這篇博客,堆主要的應(yīng)用就是解決top-k問題,在這篇文章里有具體的分析。
六、priority_queue的模擬實(shí)現(xiàn)
//仿函數(shù)
template<class T>
struct less //冰箱里的菜
{bool operator()(const T& x, const T& y) const{return x < y;}
};
template<class T>
struct greater
{bool operator()(const T& x, const T& y) const{return x > y;}
};
template<class T, class Container = vector<T>, class Compare = less<T>>//默認(rèn)是建大堆class priority_queue{public:void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (Compare()(_con[parent], _con[child]))//_con[child] > _con[parent]{std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}void adjust_down(int parent){int child = parent * 2 + 1;//建大堆,找大,假設(shè)左孩子比右孩子大while (child < _con.size()){if (child + 1 < _con.size() && (Compare()(_con[child], _con[child + 1])))//_con[child] < _con[child + 1]++child;if (Compare()(_con[parent], _con[child]))//_con[parent] < _con[child]{std::swap(_con[parent],_con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}const T& top() const{return _con[0];}void push(const T& val){_con.push_back(val);//插入后要向上調(diào)整adjust_up(_con.size() - 1);//向上調(diào)整算法}void pop(){std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);//向下調(diào)整算法}void swap(priority_queue<T>& q){_con.swap(q._con);}private:Container _con;};
? ? ? ?這邊我們用到了兩個(gè)仿函數(shù),如果是建大堆的話,用less<T>,如果是建小堆的話,用greater<T>,仿函數(shù)就是通過重載()的行為,模擬出函數(shù)的效果,比函數(shù)指針會易用很多
七、模版特化的深入分析
假設(shè)我們放了一個(gè)日期類進(jìn)去,能進(jìn)行比較嗎??
?答案是可以的,前提是我們需要在日期類重載出比較操作符
如果是以下這種情況呢??
?雖然指針也可以比較大小,但是指針比較大小的方式顯然不符合我們的預(yù)期,我們希望的是比較指針解引用的內(nèi)容,這個(gè)時(shí)候應(yīng)該怎么辦呢?
第一個(gè)方法:再寫一個(gè)專門用來比較指針解引用的仿函數(shù)
struct PDateless{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}};struct PDateGreater{bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}};
?第二個(gè)方法:對原來的less和greater進(jìn)行全特化出一個(gè)專門比較Date指針類型的仿函數(shù)
//全特化版本(因?yàn)槿鼗?#xff0c;只能針對Data*) (即食菜)template<>struct less<Date*>{bool operator()(const Date* x, const Date* y) const{return *x < *y;}};
//全特化版本(因?yàn)槿鼗?#xff0c;只能針對Data*)
template<>
struct greater<Date*>
{bool operator()(const Date* x, const Date* y) const{return *x > *y;}
};
但是以上兩種方法是不是都不太好,因?yàn)闊o論是全特化還是寫一個(gè)全新的仿函數(shù),我們都只是針對了Date*類型,但是如果我們以后遇到int* double*……難道也要再寫一個(gè)嗎??所以就有了一個(gè)更好的方法。
方法三:對原來的less和greater進(jìn)行偏特化限制出一個(gè)專門比較任意指針類型的仿函數(shù)
//偏特化版本 具體類型,針對指針這個(gè)泛類 必須在原來的基礎(chǔ)之上 (預(yù)制菜)
template<class T>
struct less<T*>
{bool operator()(const T* x, const T* y) const{return *x < *y;}
};
//偏特化版本 (針對所有類型的指針)
template<class T>
struct greater<T*>
{bool operator()(const T* x, const T* y) const{return *x > *y;}
};
?我們這個(gè)時(shí)候發(fā)現(xiàn),偏特化可以幫助我們更好地解決指針比較這樣的問題。
? ? ? 給大家舉個(gè)形象的比喻,模版就相當(dāng)于是冰箱里的菜,全特化版本就相當(dāng)于是即食菜,而偏特化就相當(dāng)于是預(yù)制菜。重新寫一個(gè)特定的仿函數(shù)就相當(dāng)于是外賣
? ? ?外賣>即食菜>預(yù)制菜>冰箱里的菜。? ?
1、 即食菜和預(yù)制菜理論上來說都是通過冰箱里的菜走出來的,所以必須要先有模版才能進(jìn)行全特化和偏特化。
2、從前往后優(yōu)先級變低。