內(nèi)容大綱:
1.C++模板簡介
1.1C++模板概觀
1.2C++函數(shù)模板
1.3C++類模板
1.4C++操作符重載
2.泛型編程
2.1概述
2.2關(guān)聯(lián)特性(Traits)
2.3迭代器(iterators)
1.C++模板簡介
1.1C++模板概觀
模板是C++一種特性,允許函數(shù)或者類(對象)通過泛型的形式表現(xiàn)或運行。
int Max(int a, int b)
{
return (a>b) ? a : b;
}
long Max(long a, long b)
{
return (a>b) ? a : b;
}
char Max( char a, char b)
{
return (a>b)? a : b;
}
倘若沒有模板涮母,那么雖然功能相同斋枢,但返回類型和參數(shù)類型不同的函數(shù)就得重寫畅涂,這無疑增加了無意義的工作锉屈。
簡單地來說,模板就像一個模具,根據(jù)不同的需求可以做出不同材質(zhì)的模型优训。
如果使用了模板,可以省去一堆冗余的代碼各聘,上述的三段代碼可以縮減成一下的表達方式:
template <typename T>
T Max( T a, T b)
{
return (a>b)?a:b;
}
使用方式也非常簡單:
int i = 1, j = 2;
Max(i, j); //編譯器會根據(jù)實參類型來推斷T是什么類型揣非,這個過程叫隱式實例化
C++主要有兩種主要的模板:
類模板 和 函數(shù)模板
實例化也有兩種類型:
顯式:在代碼中明確指出針對哪種類型進行實例化
隱式:在首次使用時根據(jù)具體情況使用一種合適的類型進行實例化
1.2C++函數(shù)模板
函數(shù)模板是參數(shù)化的一族函數(shù)。
簡而言之躲因,就是如果不考慮返回類型和輸入?yún)?shù)類型早敬,那么這些函數(shù)的功能是一樣的。
函數(shù)模板定義舉例:
template <typename T> //模板參數(shù)由關(guān)鍵字typename引入
T Max( T a, T b) //參數(shù)型別未定毛仪,以模板參數(shù)T表示
{
return (a>b)?a:b;
}
需要注意以下兩點:
1.可以用class替代typename來定義類別參數(shù)搁嗓,但struct不可以。
2.從語法上來講class 和 typename沒有區(qū)別箱靴,但是用class會導致誤解腺逛,譬如會以為只有類才能作為型別參數(shù),所以衡怀,盡量使用typename棍矛。
函數(shù)模板的使用:
int i = 1, j = 2;
float k = 1.0, l = 2.0;
Max(i, j); //T 為int
Max(k,l); //T為float
Max(i,l); //不可以,無法確定T為哪種類型
模板實例化:
用具體型別替代模板參數(shù)T的過程叫實例化抛杨,從而產(chǎn)生一個模板實例够委。
綜上,得出一個結(jié)論模板被編譯了兩次
1.一次是實例化之前怖现,檢查模板代碼本身是否有語法錯誤茁帽;
2.實例化期間玉罐,檢查對模板代碼的調(diào)用是否合法。
1.2C++類模板
與函數(shù)模板類似潘拨,類也是可以做成模板吊输,但是類模板比函數(shù)模板相比有更多的特性,所以使用起來更加靈活铁追。下面舉一個類模板的例子:
const std::size_t DefaultStackSize = 1024;
tmeplate <typenmae T, std::size_t n = DefaultStackSize>
Class Stack
{
public:
void Push(cosnt T &element);
int Pop(T &element);
int Top(T &element) const ;
private:
std::vector<T> m_Member;
std::size_t m_nMaxSize = n;
};
值得注意的是,類模板的聲明時扭屁,除了Copy constructor之外料滥,如果在類模板中需要使用到這個類本身埋泵,比如定義operator=丽声,那么應該使用其完整的定義(Stack<T,n>),而不是使用T雁社。
tmeplate <typenmae T, std::size_t n = DefaultStackSize>
Class Stack
{
public:
...
Stack(Stack<T,n> const&);
Stack<T>& operator= (Stack<T,n> const&);
...
};
如果在類外要定義一個類模板的成員函數(shù)霉撵,則要指明其是一個模板函數(shù)徒坡,例如Push函數(shù)的定義應當如下:
template<typename T, std::size_t nMaxSize>
void Stack<T, nMaxSize>::Push(const T &element)
{
...
}
類模板特化(specializations)
允許對一個類的某些模板形參類型做特化
特化的作用好處在于:
1.對于某些特俗的型別喇完,可能可以做些特別的優(yōu)化或提供不同的實現(xiàn)
2.避免在實例化類的時候引起一些可能產(chǎn)生的詭異行為
3.特化一個類模板的時候也意味著需要特化所有其他參數(shù)化的成員參數(shù)
如果要特化一個類锦溪,方法如下:
template<> //聲明一個帶template<>的類府怯,即空參數(shù)列表
class Stack<std::wstring> //在類名稱后面緊跟的尖括號中顯式指明類型
{
...
};
特化后的具體實現(xiàn)是可以和主模板的實現(xiàn)不一樣,類似函數(shù)重載则涯。
偏特化(Partial specialization)
類模板也可以被偏特化,比如主模板定義為:
template<typename T1, typename T2>
class MyCalss
{
...
};
則由此可能產(chǎn)生以下幾種主模板的偏特化:
1.將模板參數(shù)偏特化為同樣類型:
template <typename T>
class MyClass<T, T>
{
...
};
2.也可以將第二個將第二個模板參數(shù)特化為int類型粟判,不是泛化的T:
template <typename T>
class MyCalss<T, int>
{
...;
};
3.還可以將類型偏特化為指針:
template <typename T1, typename T2>
class MyClass<T1*, T2*>
{
...
};
那么知道定義后浮入,我們該如何使用呢事秀?請看下表:
但使用時需要多加小心宰衙,防止出現(xiàn)二義性出現(xiàn)供炼。
我們知道函數(shù)有默認參數(shù)袋哼,同樣涛贯,類模板與其也有異曲同工之處蔚出。例如:
template <typename T, typename TContainer = std::vector<T> >
//使用std::vector<>作為默認實參
class Stack
{
private:
TContainer m_Container;
...
};
當然骄酗,如果你傳入一個其他類型的類型時稀余,則這是將以你輸入的那個類型為T2的類型趋翻。
總而言之:
·模板類的性質(zhì)是有一個或多個類型未被指定的模板睛琳。
·對于類模板而言嘿歌,只有被調(diào)用到的成員函數(shù)才會被實例化
·類模板可以用特性的型別特化
·支持偏特化
·也允許有默認值
1.4運算符重載
對于運算符重載掸掏,由于前面兩門課已經(jīng)有所探究宙帝,并且侯老師講解得也足夠詳細丧凤,故這里不再繼續(xù)重述。
2.泛型編程
2.1概述
泛型編程是一種方法步脓,這種方式將類型以一種to-be-specified-later的方式給出浩螺。
2.2關(guān)聯(lián)特性
昨天學習C++ primer的第十章,遇到這么一個問題,程序如下:
#include<iostream>
#include<functional>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
bool check_size(const string &s1, const string &s2)
{
return s1.size()>s2.size();
}
int main ()
{
using std::placeholders::_1;
vector<string> s = {"the","quick","red","fox","jumps","ovet","the","red","slow","red","turtle"};
string a = "red";
auto p1 = bind(check_size,_1,"red");
vector<string>::iterator p = find_if(s.cbegin(),s.cend(),p1);
cout<<p-s.cbegin()+1<<endl;
}
發(fā)生報錯
[Error] conversion from '__gnu_cxx::__normal_iterator<const std::basic_string<char>*, std::vector<std::basic_string<char> > >' to non-scalar type 'std::vector<std::basic_string<char> >::iterator {aka __gnu_cxx::__normal_iterator<std::basic_string<char>*, std::vector<std::basic_string<char> > >}' requested```
后來在群里請教了別人患蹂,才知道原來我在find_if里使用了cbegin(),則find_if()傳出的迭代器也是不可修改指向的值的,所以應該是const_iterator醉顽。
所以改成
```C++
vector<string>::const_iterator p = find_if(s.cbegin(),s.cend(),p1);
就可以了。
附加內(nèi)容:
一、輸出容器元素方法:
1.利用普通for循環(huán)輸出
vector<int> v = {1,2,3,4};
for(vector<int>::iterator it = v.begin( ); it!=v.end(); it++)
{
cout<<*it;
}
2.利用范圍for循環(huán)輸出
//支持C++11的編譯器
vector<int> v = {1,2,3,4};
for(auto it:v)
{
cout<<it;
}
可以看到唆涝,利用范圍for來遍歷元素是非常方便的找都。
3.利用標準庫for_each算法和lambda表達式(方便快捷,個人比較喜歡)
vector<int> v = {1,2,3,4};
for_each( v.cbegin(), v.cend(), [](int i) { cout << i << " " ; });
4.利用流迭代器和標準庫copy算法
ostream_iterator<int> out_iter(cout, " ");
vector<int> v = {1,2,3,4};
copy( vec.cbegin( ), vec.cend( ), out_iter);
二廊酣、關(guān)于copy算法
重要的一點是傳遞給copy的目的序列至少要包含與輸入序列一樣多的元素
即不能復制元素到一個空或者元素數(shù)量少于母板的數(shù)目檐嚣。但有一點要注意,利用插入迭代器時啰扛,可以使用空的容器。
list<int> lst = {1, 2, 3, 4 };
list<int> v1, v2;
copy(lst.cbegin( ), lst.cend( ), front_inserter(lst2) );
由于有迭代器作為橋梁嗡贺,因為copy函數(shù)是先將值傳遞給迭代器隐解,再由迭代器來操作容器,這樣诫睬,容器即便是空的煞茫,經(jīng)過插入迭代器處理后可以產(chǎn)生容納該被復制元素的空間,所以可以使用空容器摄凡。