1.模板觀念與函數(shù)模板
課程主要內(nèi)容
- C++模板簡(jiǎn)介
- 泛型編程
- 容器
- 進(jìn)階
C++模板簡(jiǎn)介
??generic types:泛型。type翻譯為型別奴愉。型別更加的具體。
??簡(jiǎn)單的例子:
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;
}
??在這個(gè)函數(shù)中铁孵,T是一個(gè)abstract type锭硼,generic type,不是一個(gè)具體的類(lèi)型蜕劝。
兩種類(lèi)模板
- 類(lèi)模板(Class template)
- 函數(shù)模板(Function template)
??模板聲明的時(shí)候檀头,并未給出函數(shù)或類(lèi)的完整定義。只是提供了一個(gè)語(yǔ)法框架岖沛。
??模板的實(shí)例化是從模板構(gòu)建出一個(gè)真正的函數(shù)或類(lèi)的過(guò)程暑始,指定T真正型別的時(shí)候。
實(shí)例化(instantiation)
- 顯式實(shí)例化婴削,在代碼中明確指定
- 隱式實(shí)例化廊镜,由編譯器推導(dǎo)
C++函數(shù)模板
??是指參數(shù)化的一族函數(shù)(不止一個(gè))。
class 和 typename馆蠕,在用作定義型別參數(shù)時(shí)期升,在語(yǔ)法上沒(méi)有區(qū)別,但是在語(yǔ)義上有區(qū)別互躬,建議使用typename播赁。但是不能使用struct。
??在使用Max模板函數(shù)時(shí)吼渡,不能使用不同型別的參數(shù)來(lái)調(diào)用容为。
??用具體型別代替模板參數(shù)T的過(guò)程就叫做實(shí)例化,從而產(chǎn)生了一個(gè)模板實(shí)例寺酪。
模板被編譯了兩次
- 沒(méi)有實(shí)例化之前坎背,編譯器會(huì)檢查語(yǔ)法是否有錯(cuò)誤。
- 實(shí)例化期間寄雀,編譯器會(huì)檢查調(diào)用是否合法得滤。
參數(shù)推導(dǎo)
- 模板參數(shù)是由傳遞給模板函數(shù)的實(shí)參決定的。
- 不允許自動(dòng)型別轉(zhuǎn)換盒犹,每個(gè)T必須嚴(yán)格匹配懂更。
Max(1, 2.0);
===>
Max(static_cast<double>(1), 2.0);//將1轉(zhuǎn)換為double
Max<double>(1, 2.0);//編譯器認(rèn)為1為double
函數(shù)模板重載
??函數(shù)模板也可以像普通函數(shù)一樣被重載。普通函數(shù)可以和模板函數(shù)同同時(shí)存在(名稱(chēng)一樣)急膀,當(dāng)調(diào)用即符合普通函數(shù)的調(diào)用沮协,又符合模板函數(shù)時(shí),優(yōu)先調(diào)用普通函數(shù)卓嫂。
??所有的重載版本的聲明必須位于他們被調(diào)用位置之前慷暂。
2.類(lèi)模板與操作符重載
類(lèi)模板
??類(lèi)通過(guò)參數(shù)泛化,從而構(gòu)建出一族不同型別的類(lèi)實(shí)例晨雳。
??類(lèi)模板實(shí)參可以是某一型別或常量(僅限int或enum)行瑞,而且可以帶默認(rèn)值奸腺。
一個(gè)例子
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是編譯時(shí)的常量血久,n可以有默認(rèn)值
};
類(lèi)模板的聲明
??在類(lèi)模板內(nèi)部洋机,T可以像其他型別一樣,定義變量和成員函數(shù)洋魂。
??除了Copy constructor之外绷旗,如果在類(lèi)模板中需要使用到類(lèi)本身,如operator=副砍,應(yīng)該使用完整的定義(Stack<T, n>),而不能省略型別T衔肢。
Stack<T>& operator= (Stack<T, n> const &)
類(lèi)模板的實(shí)現(xiàn)
template<typename T, std::size_t nMaxSize>
void Stack<T, nMaxSize>::Push(const T cosnt& element){ ... }
類(lèi)模板的特化
??允許對(duì)一個(gè)類(lèi)模板的某些模板參數(shù)型別做特化。
??特化的作用或好處
- 對(duì)于某種特殊的型別豁翎,可以做一些特別的優(yōu)化或提供不同的處理方式角骤。
- 避免在實(shí)例化類(lèi)模板時(shí)引起一些可能產(chǎn)生的詭異行為。
??特化一個(gè)類(lèi)需要特化其所有參數(shù)化的成員函數(shù)心剥。
template<>
class Stack<std::wstring>{ ... };
??特化后可以添加新的成員函數(shù)邦尊,也可以改為使用list來(lái)存儲(chǔ)Stack的內(nèi)部實(shí)現(xiàn)。
偏特化
類(lèi)模板被定義為:
template <typename T1, typename T2>
class MyClass{ ... };
- 偏特化為同樣類(lèi)型:
template <typename T> class MyClass<T, T> { ... };
- 偏特化部分模板參數(shù)為指定型別:
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> |
??如果不止一個(gè)偏特化同等程度地能夠匹配某個(gè)調(diào)用优烧,那么該調(diào)用具有二義性蝉揍,編譯會(huì)報(bào)錯(cuò)。
使用 | 原型 |
---|---|
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> |
默認(rèn)模板實(shí)參
??類(lèi)似于函數(shù)的默認(rèn)參數(shù)畦娄,對(duì)于類(lèi)模板而言也可以定義其模板參數(shù)的默認(rèn)值又沾,這些值就叫做默認(rèn)模板參數(shù)。
C++操作符重載
- 不可以用operator定義一種新的操作符
- 對(duì)于內(nèi)置型別熙卡,不能再用operator重載
- 可重載為非靜態(tài)成員函數(shù)或靜態(tài)全局函數(shù)杖刷,如果該全局函數(shù)需要訪問(wèn)類(lèi)的private和protected成員,則需要聲明為friend驳癌。
- 除了operator=滑燃,所有其他操作符重載均可以被子類(lèi)繼承。
3.泛型編程(Generic Programming)
概觀
??泛型編程是一種思想颓鲜,是一種編程方法表窘。在不同的語(yǔ)言表現(xiàn)方式不一樣,在C++中使用模板的方式表現(xiàn)出來(lái)灾杰。
關(guān)聯(lián)特性 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)); //得到的結(jié)果溢出艳吠。
運(yùn)行結(jié)果(溢出):
Sigma(str) = 38
??為每個(gè)Sigma函數(shù)的參數(shù)型別創(chuàng)建一種關(guān)聯(lián)(association),關(guān)聯(lián)的型別就是用來(lái)存儲(chǔ)Sigma結(jié)果的型別孽椰。
??這種關(guān)聯(lián)可以看做是型別T的一種特性(characteristic of the type T)昭娩,此種型別可以稱(chēng)作T的trait凛篙。
??Trais可以實(shí)現(xiàn)為模板類(lèi),association則是針對(duì)每個(gè)具體型別T的特化栏渺。
template<typename T> class SigmaTraits{};
template<> class SigmaTraits<char>{
public: typedef int ReturnType;
};
template<> class SigmaTraits<short>{
public: typedef int ReturnType;
};
...
修改后的Sigma函數(shù):
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;
}
修改后的執(zhí)行結(jié)果:
Sigma(str) = 294
??雖然此時(shí)傳入?yún)?shù)T的型別是char呛梆,但是返回類(lèi)型是int。原因就是使用了Traits磕诊。
迭代器
??迭代器是指泛化的指針填物,迭代器本身是一個(gè)對(duì)象,指向另外一個(gè)(可以被迭代的)對(duì)象霎终。
??在STL中迭代器是容器和算法之間的接口滞磺。
基本思想
- 分離算法和容器,不需要相互依賴(lài)莱褒。
- 粘合算法和容器击困,使得一種算法的實(shí)現(xiàn)可以運(yùn)用到多種不同的容器上。
- 每種容器都有其對(duì)應(yīng)的迭代器广凸。
4.容器(上)
Vector
??Vector是一種可以存放任意型別的動(dòng)態(tài)數(shù)組阅茶,連續(xù)的內(nèi)存空間。
#include<vector>//使用的時(shí)候谅海,不要加.h
訪問(wèn)vector的元素:
- vector::at() //有數(shù)組越界檢查脸哀,效率低。
- vector::operator[] //不檢查扭吁,效率高企蹭。
刪除vector的元素:
- clear:清除整個(gè)vector
- pop_back:彈出vector尾部元素
- erase:刪除vector某一位置元素
v.erase(
std::remove_if(
v.begin(),
v.end(),
ContainsString(L"C++")
),
v.end());
??std::remove_if函數(shù)返回了一個(gè)迭代器,需要?jiǎng)h除的元素的位置智末,remove_if函數(shù)需要一個(gè)條件函數(shù)谅摄,條件函數(shù)是一個(gè)派生自std::unary_function的一個(gè)仿函數(shù),返回true或false來(lái)決定該元素是否是否會(huì)被刪除系馆。
Deque
??Deque是一種可以存放任意型別的雙向隊(duì)列送漠。
??Deque提供的函數(shù)與vector類(lèi)似,新增了兩個(gè)函數(shù):
- push_front:在頭部插入一個(gè)元素
- pop_front:在頭部彈出一個(gè)元素
List
??List是一種可以存放任意型別的雙向鏈表(doubly linked list)由蘑。內(nèi)存中地址不連續(xù)闽寡。
List的優(yōu)勢(shì):
- List的優(yōu)勢(shì)在于其彈性,可以隨意插入和刪除元素尼酿,僅僅改變節(jié)點(diǎn)前項(xiàng)和后項(xiàng)的鏈接爷狈。
- 對(duì)于插入、刪除和替換等裳擎,效率極高涎永。
- 通常只改變鏈接,沒(méi)有元素復(fù)制。
List的劣勢(shì):
- 只能以連續(xù)的方式存取List中的元素羡微。
- 對(duì)于查找谷饿、隨機(jī)存取等元素定位,效率低妈倔。
splice
list::splice實(shí)現(xiàn)list拼接的功能博投。將源list的內(nèi)容部分或全部元素刪除,拼插入到目的list盯蝴。
函數(shù)有以下三種聲明:
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 );
函數(shù)說(shuō)明:在list間移動(dòng)元素:
- 將x的元素移動(dòng)到目的list的指定位置毅哗,高效的將他們插入到目的list并從x中刪除。
- 目的list的大小會(huì)增加捧挺,增加的大小為插入元素的大小黎做。x的大小相應(yīng)的會(huì)減少同樣的大小。
- 前兩個(gè)函數(shù)不會(huì)涉及到元素的創(chuàng)建或銷(xiāo)毀松忍。第三個(gè)函數(shù)會(huì).
- 指向被刪除元素的迭代器會(huì)失效蒸殿。
參數(shù):
- position:目的list的位置,用來(lái)標(biāo)明 插入位置鸣峭。
- x :源list宏所。
- first,last:x里需要被移動(dòng)的元素的迭代器。區(qū)間為[first, last)摊溶,包含first指向的元素爬骤,不包含last指向的元素。