【GeekBand】C++面向?qū)ο蟾呒壘幊?第七周筆記

內(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*>
{
...
};

那么知道定義后浮入,我們該如何使用呢事秀?請看下表:


偏特化使用示例.png

但使用時需要多加小心宰衙,防止出現(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)生容納該被復制元素的空間,所以可以使用空容器摄凡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末续徽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亲澡,更是在濱河造成了極大的恐慌钦扭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件床绪,死亡現(xiàn)場離奇詭異客情,居然都是意外死亡其弊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門膀斋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梭伐,“玉大人,你說我怎么就攤上這事仰担『叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵摔蓝,是天一觀的道長赂苗。 經(jīng)常有香客問我,道長项鬼,這世上最難降的妖魔是什么哑梳? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮绘盟,結(jié)果婚禮上鸠真,老公的妹妹穿的比我還像新娘。我一直安慰自己龄毡,他們只是感情好吠卷,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沦零,像睡著了一般祭隔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上路操,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天疾渴,我揣著相機與錄音,去河邊找鬼屯仗。 笑死搞坝,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的魁袜。 我是一名探鬼主播桩撮,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼峰弹!你這毒婦竟也來了店量?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鞠呈,失蹤者是張志新(化名)和其女友劉穎融师,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粟按,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡诬滩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年霹粥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疼鸟。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡后控,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出空镜,到底是詐尸還是另有隱情浩淘,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布吴攒,位于F島的核電站张抄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洼怔。R本人自食惡果不足惜署惯,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镣隶。 院中可真熱鬧极谊,春花似錦、人聲如沸安岂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽域那。三九已至咙边,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間次员,已是汗流浹背败许。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淑蔚,地道東北人檐束。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像束倍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盟戏,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容