GeekBand STL與泛型編程 第一周

1.模板觀念與函數模板

課程主要內容

  • C++模板簡介
  • 泛型編程
  • 容器
  • 進階

C++模板簡介

??generic types:泛型。type翻譯為型別。型別更加的具體。

??簡單的例子:

int Max(int a, int b){
    return (a > b) ? a : b;
}

long Max(long a, long b){
    return (a > b) ? a : b;
}
...

===>

template<typename T>
T Max(T a, T b){
    return (a > b) ? a : b;
}

??在這個函數中,T是一個abstract type,generic type,不是一個具體的類型。

兩種類模板

  • 類模板(Class template)
  • 函數模板(Function template)

??模板聲明的時候,并未給出函數或類的完整定義。只是提供了一個語法框架。

??模板的實例化是從模板構建出一個真正的函數或類的過程,指定T真正型別的時候。

實例化(instantiation)

  • 顯式實例化,在代碼中明確指定
  • 隱式實例化,由編譯器推導

C++函數模板

??是指參數化的一族函數(不止一個)。

class 和 typename,在用作定義型別參數時,在語法上沒有區別,但是在語義上有區別,建議使用typename。但是不能使用struct。

??在使用Max模板函數時,不能使用不同型別的參數來調用。

??用具體型別代替模板參數T的過程就叫做實例化,從而產生了一個模板實例。

模板被編譯了兩次

  • 沒有實例化之前,編譯器會檢查語法是否有錯誤。
  • 實例化期間,編譯器會檢查調用是否合法。

參數推導

  • 模板參數是由傳遞給模板函數的實參決定的。
  • 不允許自動型別轉換,每個T必須嚴格匹配。
Max(1, 2.0);

===>

Max(static_cast<double>(1), 2.0);//將1轉換為double

Max<double>(1, 2.0);//編譯器認為1為double

函數模板重載

??函數模板也可以像普通函數一樣被重載。普通函數可以和模板函數同同時存在(名稱一樣),當調用即符合普通函數的調用,又符合模板函數時,優先調用普通函數。

??所有的重載版本的聲明必須位于他們被調用位置之前。

2.類模板與操作符重載

類模板

??類通過參數泛化,從而構建出一族不同型別的類實例。

??類模板實參可以是某一型別或常量(僅限int或enum),而且可以帶默認值。

一個例子

const std::size_t DefaultStackSize = 1024;
template<typename T, std::size_t n = DefaultStackSize>
class Stack{
public:
    void Push(const T cosnt& element);
    int Pop(T& element);
    int Top(T& element) cosnt;
private:
    std::vector<T> m_Members;
    std::size_t m_nMaxSize = n; //n是編譯時的常量,n可以有默認值
};

類模板的聲明

??在類模板內部,T可以像其他型別一樣,定義變量和成員函數。

??除了Copy constructor之外,如果在類模板中需要使用到類本身,如operator=,應該使用完整的定義(Stack<T, n>),而不能省略型別T。

Stack<T>& operator= (Stack<T, n> const &)

類模板的實現

template<typename T, std::size_t nMaxSize>
void Stack<T, nMaxSize>::Push(const T cosnt& element){ ... }

類模板的特化
??允許對一個類模板的某些模板參數型別做特化。

??特化的作用或好處

  • 對于某種特殊的型別,可以做一些特別的優化或提供不同的處理方式。
  • 避免在實例化類模板時引起一些可能產生的詭異行為。

??特化一個類需要特化其所有參數化的成員函數。

template<>
class Stack<std::wstring>{ ... };

??特化后可以添加新的成員函數,也可以改為使用list來存儲Stack的內部實現。

偏特化

類模板被定義為:

template <typename T1, typename T2> 
class MyClass{ ... };
  • 偏特化為同樣類型:
template <typename T> class MyClass<T, T> { ... };
  • 偏特化部分模板參數為指定型別:
template <typename T> class MyClass<T, int> { ... };
  • 偏特化為指針:
template <typename T1, typename T2> 
class MyClass<T1*, T2*>{ ... };
使用 原型
MyClass<int, float> obj; MyClass<T1, T2>
MyClass<float, float> obj; MyClass<T, T>
MyClass<float, int> obj; MyClass<T, int>
MyClass<int, float> obj; MyClass<T1, T2>

??如果不止一個偏特化同等程度地能夠匹配某個調用,那么該調用具有二義性,編譯會報錯。

使用 原型
MyClass<int, int> obj; Error matches MyClass<T, T> and MyClass<T, int>
MyClass<int, int> obj; Error matches MyClass<T, T> and MyClass<T1, T2>

默認模板實參

??類似于函數的默認參數,對于類模板而言也可以定義其模板參數的默認值,這些值就叫做默認模板參數。

C++操作符重載

  • 不可以用operator定義一種新的操作符
  • 對于內置型別,不能再用operator重載
  • 可重載為非靜態成員函數或靜態全局函數,如果該全局函數需要訪問類的private和protected成員,則需要聲明為friend。
  • 除了operator=,所有其他操作符重載均可以被子類繼承。

3.泛型編程(Generic Programming)

概觀

??泛型編程是一種思想,是一種編程方法。在不同的語言表現方式不一樣,在C++中使用模板的方式表現出來。

關聯特性 Traits

什么是traits,以及為什么使用traits?

template<typename T>
T Sigma(const T* begin, const T* end){
    T total = T();
    while(end != begin){
        total += *begin++;
    }
    return total;
}
char str[] = "abc";
int length = strlen(str);
char* p = str;
char* e = str + length;
printf("Sigma(str) = %d\n", Sigma(p, q)); //得到的結果溢出。    

運行結果(溢出):

Sigma(str) = 38

??為每個Sigma函數的參數型別創建一種關聯(association),關聯的型別就是用來存儲Sigma結果的型別。

??這種關聯可以看做是型別T的一種特性(characteristic of the type T),此種型別可以稱作T的trait。

??Trais可以實現為模板類,association則是針對每個具體型別T的特化。

template<typename T> class SigmaTraits{};
template<> class SigmaTraits<char>{
    public: typedef int ReturnType;
};
template<> class SigmaTraits<short>{
    public: typedef int ReturnType;
};
...

修改后的Sigma函數:

template<typename T>
typename SigmaTraits<T>::ReturnType Sigma(const T* begin, const T* end){
    typedef SigmaTraits<T>::ReturnType ReturnType;
    ReturnType total = ReturnType();
    while(end != begin){
        total += *begin++;
    }
    return total;
}

修改后的執行結果:

Sigma(str) = 294

??雖然此時傳入參數T的型別是char,但是返回類型是int。原因就是使用了Traits。

迭代器

??迭代器是指泛化的指針,迭代器本身是一個對象,指向另外一個(可以被迭代的)對象。

??在STL中迭代器是容器和算法之間的接口。

基本思想

  • 分離算法和容器,不需要相互依賴。
  • 粘合算法和容器,使得一種算法的實現可以運用到多種不同的容器上。
  • 每種容器都有其對應的迭代器。

4.容器(上)

Vector

??Vector是一種可以存放任意型別的動態數組,連續的內存空間。

#include<vector>//使用的時候,不要加.h

訪問vector的元素:

  • vector::at() //有數組越界檢查,效率低。
  • vector::operator[] //不檢查,效率高。

刪除vector的元素:

  • clear:清除整個vector
  • pop_back:彈出vector尾部元素
  • erase:刪除vector某一位置元素
v.erase(
    std::remove_if(
        v.begin(),
        v.end(),
        ContainsString(L"C++")
    ),
    v.end());

??std::remove_if函數返回了一個迭代器,需要刪除的元素的位置,remove_if函數需要一個條件函數,條件函數是一個派生自std::unary_function的一個仿函數,返回true或false來決定該元素是否是否會被刪除。

Deque

??Deque是一種可以存放任意型別的雙向隊列。

??Deque提供的函數與vector類似,新增了兩個函數:

  • push_front:在頭部插入一個元素
  • pop_front:在頭部彈出一個元素

List

??List是一種可以存放任意型別的雙向鏈表(doubly linked list)。內存中地址不連續。

List的優勢:

  • List的優勢在于其彈性,可以隨意插入和刪除元素,僅僅改變節點前項和后項的鏈接。
  • 對于插入、刪除和替換等,效率極高。
  • 通常只改變鏈接,沒有元素復制。

List的劣勢:

  • 只能以連續的方式存取List中的元素。
  • 對于查找、隨機存取等元素定位,效率低。

splice

list::splice實現list拼接的功能。將源list的內容部分或全部元素刪除,拼插入到目的list。

函數有以下三種聲明:

void splice ( iterator position, list<T,Allocator>& x );

void splice ( iterator position, list<T,Allocator>& x, iterator i );

void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );

函數說明:在list間移動元素:

  • 將x的元素移動到目的list的指定位置,高效的將他們插入到目的list并從x中刪除。
  • 目的list的大小會增加,增加的大小為插入元素的大小。x的大小相應的會減少同樣的大小。
  • 前兩個函數不會涉及到元素的創建或銷毀。第三個函數會.
  • 指向被刪除元素的迭代器會失效。

參數:

  • position:目的list的位置,用來標明 插入位置。
  • x :源list。
  • first,last:x里需要被移動的元素的迭代器。區間為[first, last),包含first指向的元素,不包含last指向的元素。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容