編譯器在看到模版定義時不產生**,這與看到類定義不產生**一樣,只會在看到用模版產生類並使用了類的物件、或呼叫了函式模版是,編譯器才產生特定的模版例項,這一過程也稱為例項化。
普通函式和類的成員函式的定義一般可以放在原始檔中,而函式宣告和類定義需要放在標頭檔案中。由於模版需要進行例項化,編譯器必須訪問定義模版的原始碼。標準c++為編譯模版**定義了兩種模型,分別為「包含編譯模型」和「分別編譯模型」。要編譯定義的類模版和函式模板,必須了解所使用的編譯器是如何處理例項化的。
//header file, util.h
#ifndef util_h
#define uitl_h
//declarations
template t> int cmp(const t &, const t &);
...//include the implements source file
#include "util.cpp"
#endif
上述**中能夠保持標頭檔案和原始檔的分離,但必須在標頭檔案最後加上include語句包含相關原始檔,保證編譯器在編譯使用模版的**時能夠看到兩種檔案。
上述包含結構本質上可以當做是模版的宣告和定義都存在於標頭檔案中,不區分宣告和定義,合二為一。對於c++的stl,都是在標頭檔案中將模版直接定義的。
編譯器在分別編譯模型中會跟蹤相關模版定義,但必須讓編譯器知道要記住給定的模版定義,這是通過export關鍵字完成的。export關鍵字不必出現在模版宣告中,只需在模版定義的template關鍵字前使用即可。
//函式模版的宣告
templatet> t sum(t t1, t t2);
//函式模版的定義
export templatet> t sum(t t1, t t2)
類模版在標頭檔案中不必使用export,否則該標頭檔案只能被程式中的乙個原始檔使用。只需在類的實現檔案中使用export:
//header file
templatet> class queue;
//source file
export templatet> class queue;
#include "queue.h"
//queue member definitions
...
模版可以作為類、結構或者其他模版的成員,可以將用於常規類的特性全用於模板類(如基類,元件類,型別形參)。
在使用類模版的名字的時候,必須指定模版形參。例外的是:在類模版的作用域內部,可以使用類模版的非限定名。
templatet>
class queue
queue(const queue &)
...void push(const t &);
...private:
queueitem * head;
...};template t>
void queue::push(const t & x)
從上述可以看出,在類的內部,建構函式可以直接使用queue,本質上編譯器推斷的是queue
,這僅僅對於同名來說符合,但對於類的模版成員必須明確指出queueitem
。
在stl的完全設計實現中,在類或者模版類中使用模版作為成員這項特性是必不可少的。這裡可以是模版類或者模版函式,下面示例就是使用模版建構函式:
templatet>
class a
;//類外部定義成員模版函式
tempaltet> templatea::a(i beg, i end)
上述定義中,建構函式提供的兩種不同特定型別的都可以在底層使用模版建構函式實現,呼叫int建構函式時可以呼叫初始化列表的建構函式,進而又可以呼叫模版建構函式傳入迭代器來實現。
除此之外,上述定義中使用了類模版b作為成員,也就是完全可以將類模版作為普通類來進行使用。
模版可以包含型別引數和非型別引數,這些引數本身也可以是模版。在stl的實現中使用的也非常廣泛。
template< templatet>class b>
class a;
上述定義中的模版類a的模版引數是乙個模版類,引數為b。因此例項化模版類a時必須傳入乙個模版類作為實參。
aobj; //例項化模版類a
//例項化時傳入的模版實參c必須有如下的定義才能匹配
templatet>
class c;
上述模版類c例項化a後,產生的obj物件,將會有兩個資料成員,分別是:cs1
和cs2
。
模版類可以有友元,分為三類:
非模版友元:模板類中常規宣告的友元
普通的常規函式,宣告為友元即可,可以攜帶模版引數也可不攜帶:
templatet>
class hf
;
method1將是所有例項化的模版類hf的友元函式,為訪問模版類的成員,可以使用全域性物件、全域性指標,或者建立自己的模版類物件,也可以訪問靜態資料成員。
約束模版友元:友元的型別取決於模板類被例項化時的型別
友元本身為模版時,就可以與模版例項化同步。模版類可以只授予對特定例項的訪問權,模版類的引數與友元的模版引數相同,或者指定特定型別的友元。
template
void method1(const t &);
template
class tplcls;
template
class hf
;
非約束模版友元:友元的所有具體化都是類的每乙個具體化的友元
通過在模版類的內部宣告新的模版,就能建立非約束的友元,每個例項化都是每個類例項化的友元,友元模版型別引數與模板類的型別引數是不同的:
template
class hf
;
上述定義中,method3與ff與模板類使用不同的模版形參,ff和method3的任意例項均可以訪問hf的任意例項的私有成員。這種宣告在hf與其友元method3和ff的每個例項之間建立了一對多的對映,也就是對hf 的每個例項,method3和ff的每個例項都是其友元。
最後給出queue和queueitem的模版友元宣告:
templatet> class
queue;
templatetype>
class
queueitem;
模版特化或者成為具體化是指:泛化的模版型別引數可以指定乙個特殊的型別,同時在模版引數列表中去掉相應的模版形參,這是為了針對特定型別的類進行特殊處理,這種特性在stl中的traits技法中進行了巧妙的利用。分為偏特化和全特化。
特化的版本對於使用者來說是透明的,編譯器會自動優先匹配特化版本,與使用從通用模版例項化得到的版本沒有區別。模版特化版本必須讓所有使用的原始碼可見,因此需要與模版泛型版本一起放在標頭檔案中。
將所有模版形參都用特定型別替換,移除所有模版形參。
tempalteclass pair;
//全特化版本
template<> class pair;
string pair::first()
在類的外部定義全特化版本的成員時,不能新增template<>
標記。
另外,可以僅僅特化類的部分成員,這樣類的例項化時使用通用定義,當呼叫特化版本的函式時,如果型別與特化型別匹配,則會呼叫特化版本的成員:
template<> string pair::first() //特化版本的成員在類外部單獨定義
//部分特化版本
templateclass pair;
paira;
pairb;
在模板類名稱後的尖括號內t1作為佔位符,實參char *
作為t2的特化型別。對於部分特化,物件a和b雖然都匹配到通用的版本,但是物件b存在偏特化版本,因此會優先匹配,物件a只會匹配通用版本。
偏特化即partial initialization,是指針對部分模版形參指定某種特定限制,其他模版形參保持泛化特性。
template
class ca;
template
class ca;
template
class ca;
(22) 事件總結
qt 的事件是整個 qt 框架的核心機制之一,也比較複雜。說它複雜,更多是因為它涉及到的函式眾多,而處理方法也很多,有時候讓人難以選擇。現在我們簡單總結一下 qt 中的事件機制。qt 中有很多種事件 滑鼠事件 鍵盤事件 大小改變的事件 位置移動的事件等等。如何處理這些事件,實際有兩種選擇 1 所有事...
博弈類模版,總結
博弈類的核心在於獲取sg函式。對於乙個局面,若至少有一種操作使它變為p局面,則它是n局面 對於乙個局面,無論如何操作都使它變為n局面,則它是p局面 將遊戲中間的狀態看做頂點,將狀態的轉移看做邊 sg v 定義為 沒有出現在中的最小自然數 初始節點 必須是p節點 沒有出邊,sg 0.若v有邊指向某sg...
模版引擎總結之綜合分析模版引擎
方式一 var str function template filename,data filename代表模版檔案的路徑 方式二 template.compile source,options 返回渲染函式 方式三 template.render source,data,options 返回渲染結...