模板和标准模板库
template<class T>
class Array{
//...
};
T就是類(lèi)參數(shù),T前面的關(guān)鍵字是class或typename。
類(lèi)參數(shù)是個(gè)符號(hào),代表通過(guò)關(guān)鍵字class或typename將要被某個(gè)確定類(lèi)型代替的類(lèi)型
template和<之間可以有一個(gè)空格,但是通常不要有
我們將Array<double>和Array<char>稱(chēng)為模板類(lèi)Array<T>的模板實(shí)例
不可能存在一個(gè)類(lèi)型為Array或Array<T>的對(duì)象,但我們可以定義類(lèi)型為Array<int>的對(duì)象。也就是說(shuō),一個(gè)對(duì)象不能屬于像Array這樣的模板類(lèi),但可以屬于Array<char>
這樣的模板類(lèi)實(shí)例
用內(nèi)建或自定義的數(shù)據(jù)類(lèi)型都可以創(chuàng)建模板實(shí)例
模板類(lèi)可以作為一種數(shù)據(jù)類(lèi)型出現(xiàn)在參數(shù)列表中
template<class T>
ostream & operator<<(ostream& os,const Array<T>& ar) {
for(int i = 0 ;i < ar.get_size() ; i ++)
os << ar[i] << endl;
return os;
}
模板類(lèi)必須至少有一個(gè)類(lèi)參數(shù),當(dāng)然可以有多個(gè)類(lèi)參數(shù)。模板類(lèi)還可以有非類(lèi)參數(shù)的參數(shù)。一般稱(chēng)之為函數(shù)類(lèi)型參數(shù)(無(wú)類(lèi)型模板參數(shù)),一個(gè)模板類(lèi)可以有多個(gè)函數(shù)類(lèi)型參數(shù),這些參數(shù)的數(shù)據(jù)類(lèi)型可以使內(nèi)建類(lèi)型或自定義類(lèi)型參數(shù)
template<class T,int x,int y>
class Sample {
};
用具體的數(shù)據(jù)類(lèi)型代替模板頭中的類(lèi)參數(shù),并用具體的數(shù)值代替模板頭中的函數(shù)類(lèi)型參數(shù),就可以實(shí)例化一個(gè)模板類(lèi),所以模板類(lèi)有時(shí)稱(chēng)為參數(shù)化的類(lèi)
模板類(lèi)中的成員函數(shù)可以以?xún)?nèi)聯(lián)函數(shù)的形式定義
模板類(lèi)中的函數(shù)類(lèi)型參數(shù)是類(lèi)參數(shù)中除了class或typename定義的類(lèi)型
例子:
頭文件stack.h:
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
template<class T>
class Stack {
public:
enum {DefaultStack=50,EmptyStack=-1};
Stack();
Stack(int);
~Stack();
void push(const T&);
T pop();
T topNoPop() const;
bool empty() const;
bool full() const;
private:
T *elements;
int top;
int size;
void allocate() {
elements = new T[size];
top = EmptyStack;
}
void msg(const char *m) const {
cout << "*** " << m << " ***" << endl;
}
friend ostream& operator<<(ostream&,const Stack<T> &);
};
template<class T>
Stack<T>::Stack() {
size = DefaultStack;
allocate();
}
template<class T>
Stack<T>::Stack(int s) {
if(s < 0)
s *= -1;
else if( 0 == s)
s = DefaultStack;
size = s;
allocate();
}
template<class T>
Stack<T>::~Stack() {
delete[] elements;
}
template<class T>
void Stack<T>::push(const T& e) {
assert(!full());
if(!full())
elements[++top] = e;
else
msg("Stack full!");
}
template<class T>
T Stack<T>::pop() {
assert(!empty());
if(!empty())
return elements[top --];
else {
msg("Stack empty!");
T dummy_value = 0;
return dummy_value;
}
}
template<class T>
T Stack<T>::topNoPop() const {
assert(top > EmptyStack);
if(!empty())
return elements[top];
else {
msg("Stack empty!");
T dummy_value = 0;
return dummy_value;
}
}
template<class T>
bool Stack<T>::empty() const {
return top <= EmptyStack;
}
template<class T>
bool Stack<T>::full() const {
return top+1 >= size;
}
template<class T>
ostream& operator<<(ostream& os,const Stack<T> &s) {
s.msg("Stack conents:");
int t = s.top;
while(t > s.EmptyStack)
cout << s.elements[t--] << endl;
return os;
}
測(cè)試文件:
結(jié)果:
標(biāo)準(zhǔn)模板庫(kù)(Standard Template Library)STL是標(biāo)準(zhǔn)C++庫(kù)的一部分。STL的模板類(lèi)為C++提供了完善的數(shù)據(jù)結(jié)構(gòu)
容器、算法、和迭代器是STL的三個(gè)基本組成部分。
一個(gè)STL容器是對(duì)象的集合。
STL包括vector、stack、queue、deque、list、set和map等
STL算法是對(duì)容器進(jìn)行處理的函數(shù),例如:copy、sort、search、merge和permute等
STL迭代器是一訪問(wèn)器中的一種機(jī)制,一次訪問(wèn)一個(gè)對(duì)象
STL的迭代器可分為 正向、反向、雙向和隨機(jī)遍歷
和C++數(shù)組不同,STL容器的大小自動(dòng)變化。
STL基本容器可以分兩組:序列式容器和關(guān)聯(lián)式容器
例子:vector容器
vector支持兩端插入,并提供begin和end成員函數(shù),分別用來(lái)訪問(wèn)頭部和尾部元素。兩個(gè)函數(shù)都返回一個(gè)可隨機(jī)訪問(wèn)的迭代器
begin:
如果容器不為空,指向容器的第一個(gè)元素
如果容器為空,指向容器尾部之后的位置
end:
僅指向容器尾部之后的位置
vector和deque的區(qū)別主要在于他們的底層實(shí)現(xiàn)不同,特別是插入和刪除操作的實(shí)現(xiàn)機(jī)制不同。
對(duì)于vector來(lái)說(shuō),不管其大小時(shí)多少,在頭部插入的效率總是比在尾部插入的效率低。在尾部插入將耗費(fèi)固定的時(shí)間。在頭部插入時(shí),耗時(shí)與vector大小乘正比。
deque與vector不同,不管插入還是刪除操作,也不管這些操作是頭部還是在尾部進(jìn)行,算法效率是固定的
例子:list容器
list有兩種迭代器:
list<type>::iterator
list<type>::const_iterator
type是我們要定義的類(lèi)型
vector、deque和list
三者都是STL的基本序列式容器:都有insert和erase成員函數(shù)
vector和deque都重載了[]而list沒(méi)有,因此我們用list時(shí)要使用迭代器
另外效率上也有差別:
STL的關(guān)聯(lián)式容器:set和map
set是一種集合,其中包含0個(gè)或多個(gè)不重復(fù)的和不排序的元素,這些元素被稱(chēng)為鍵值
例子:(set)
set重載了而多個(gè)insert成員函數(shù),我們既可以向vector那樣采用指定插入位置,也可以不指定
s.insert(s.begin(),66);
s.insert(s.end(),99);
s.insert(33);
不論采用哪種重載方式,insert均能保證在set中不含重復(fù)的鍵
find是set中另一個(gè)常用的函數(shù)用來(lái)查看set中是否包含某個(gè)指定的鍵值,如果存在返回指向該鍵的迭代器,否則返回end成員函數(shù)的值
例子:(map)
map也定義了insert函數(shù),map還重載了[]操作符
容器適配器是基本容器的衍生物,并利用基本容器來(lái)實(shí)現(xiàn)其特定功能。
容器適配器有三種:stack,queue和priority_queue
stack適配器用于創(chuàng)建LIFO列表。queue適配器用于創(chuàng)建FIFO列表
priority_queue用于創(chuàng)建帶優(yōu)先級(jí)的隊(duì)列,其元素以某種優(yōu)先順序進(jìn)行刪除
例子:(stack)
默認(rèn)情況下STL stack衍生自deque因此
stack<char> s;
等價(jià)于
stack<char, deque<char> > s;
如果需要讓stack從vector衍生,可采用如下定義方式:
stack<char , vector<char> > s;
stack中的
top函數(shù)返回棧頂元素
pop函數(shù)刪除棧頂元素
兩個(gè)通常配對(duì)使用top一個(gè)元素判斷后,再pop
例子:(queue)
和stack相同,默認(rèn)情況下,queue衍生自deque。
queue也有push和pop成員函數(shù)
同時(shí)提供了front成員函數(shù),用來(lái)訪問(wèn)queue頭元素
例子: (priority_queue)
在默認(rèn)情況下,priority_queue衍生自vector
優(yōu)先隊(duì)列是指該隊(duì)列以某種優(yōu)先級(jí)順序刪除隊(duì)列中的元素。例如,一個(gè)整數(shù)優(yōu)先級(jí)隊(duì)列可能以降序方式來(lái)刪除元素。這樣就保證了在刪除元素的過(guò)程中保持隊(duì)列元素的優(yōu)先級(jí)順序
priority_queue使用我們熟悉的push和pop成員函數(shù)來(lái)插入和刪除。top返回不刪除
我們熟悉的string類(lèi)以及bitset類(lèi)也可以做STL容器來(lái)使用:
例子:(bitset)
bitset是二進(jìn)制序列。bitset并不是數(shù)學(xué)意義上的集合,因?yàn)樗梢园貜?fù)的數(shù)字(畢竟只有0和1)
由于bitset默認(rèn)構(gòu)造函數(shù)將所有的位都清0,
但是可以用轉(zhuǎn)型構(gòu)造函數(shù)初始一個(gè)bitset的值 bitset(unsigned long n);
bitset<8> bs(9) 就創(chuàng)建一個(gè)00001001的bitset
bitset可完成如下功能
1>將某個(gè)指定位設(shè)置為0或1
2>對(duì)某個(gè)位或所有位取反
3>測(cè)試某個(gè)位為0或1
4>進(jìn)行左移或右移操作
5>進(jìn)行與、或和異或等標(biāo)準(zhǔn)二進(jìn)制操作
6>將bitset轉(zhuǎn)型為string或unsigned long類(lèi)型
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
const featureCount = 9;
const unsigned Framed = 1;
const unsigned Bordered = 2;
const unsigned StdMenu = 4;
const unsigned ToolBar = 8;
const unsigned StatusBar= 16;
class Window {
public:
Window(const string &n,unsigned f) {
name = n;
features = bitset<featureCount>(f);
createWindow();
}
Window(const char *n ,unsigned f) {
name = n;
features = bitset<featureCount>(f);
createWindow();
}
private:
void createWindow() {
cout << "\n*** Windows feature for " << name
<< " given bit mask " << features << ":" << endl;
if(features[0])
cout << "\t" << "framed" << endl;
if(features[1])
cout << "\t" << "bordered" << endl;
if(features[2])
cout << "\t" << "with standard menu" << endl;
if(features[3])
cout << "\t" << "with tool bar" << endl;
if(features[4])
cout << "\t" << "with status bar" << endl;
}
string name;
bitset<featureCount> features;
};
int main() {
Window w1("w1",Framed | ToolBar | StatusBar);
Window w2("w2",ToolBar | Framed | StatusBar);
Window w3("w3",Framed | StdMenu | StatusBar | ToolBar | Bordered );
return 0;
}
結(jié)果:
STL有大量用來(lái)處理容器的算法。這些算法可以分為如下幾類(lèi):排序和搜索、數(shù)值處理、集合運(yùn)算、復(fù)制等
STL算法是用模板函數(shù)實(shí)現(xiàn)的,如STL的reverse算法:
template<class BidirectionalIterator >
void reverse(BidirectionalIterator it1,BidirectionalIterator it2);
STL算法使用迭代器來(lái)變量容器,并在迭代過(guò)程中處理容器中的元素:
例子1:
例子2:
STL算法nth_element將序列中的中值元素(median elememt)放置到序列的中間位置。僅僅是找到并放到中間位置,不會(huì)對(duì)序列排序,結(jié)果我們可以看到
nth_element有三個(gè)參數(shù),第一個(gè)迭代器標(biāo)志序列頭部,第二個(gè)要放置的位置,最后一個(gè)迭代器標(biāo)志序列的尾部
STL的copy算法三個(gè)參數(shù),前兩個(gè)是迭代器的范圍,最后一個(gè)是輸出型迭代器:
輸出迭代器實(shí)例:ostream_iterator<char>
表達(dá)式ostream_iterator<char>(cout," ")調(diào)用帶參數(shù)的構(gòu)造函數(shù)來(lái)創(chuàng)建迭代器,這個(gè)構(gòu)造函數(shù)的第一個(gè)參數(shù)為輸出流,本來(lái)為cout,第二個(gè)參數(shù)是可選的,用來(lái)指定輸出到輸出流的各項(xiàng)之間的分隔符,本例為空格符
輸出迭代器例子:
STL除了上面的還有其他構(gòu)件:
函數(shù)對(duì)象(function object)、函數(shù)適配器(function adaptor) 和 STL alocator
函數(shù)對(duì)象是一個(gè)類(lèi)對(duì)象,它對(duì)函數(shù)調(diào)用操作符() 進(jìn)行重載,并且該重載函數(shù)是公有的。可用STL的函數(shù)對(duì)象來(lái)代替普通函數(shù)
上面的dump函數(shù):
void dump(int i) { cout << i << endl;}
在該程序中將dump作為for_each第三個(gè)參數(shù)
for_each(v.begin(),v.end(),dump);
作為另一種實(shí)現(xiàn)方式,我們可以設(shè)計(jì)一個(gè)函數(shù)對(duì)象:
template<clasls T>
struct dumpIt {
void operator()(T arg) { cout << arg << endl; }
};
然后調(diào)用這個(gè)模板類(lèi)的int型實(shí)例的重載調(diào)用操作符
for_each(v.begin(),v.end(),dumpIt<int>());
由于在默認(rèn)的情況下結(jié)構(gòu)的成員是公有的,因此我們使用關(guān)鍵字struct來(lái)創(chuàng)建dumpIt類(lèi)。之所以將函數(shù)對(duì)象的函數(shù)調(diào)用操作符設(shè)計(jì)為公有的,是因?yàn)橐诤瘮?shù)對(duì)象的定義之外使用它
一個(gè)函數(shù)對(duì)象的開(kāi)銷(xiāo)比指針傳遞函數(shù)的開(kāi)銷(xiāo)小,所以函數(shù)對(duì)象通常比常規(guī)函數(shù)執(zhí)行速度更快
函數(shù)適配器是用來(lái)以現(xiàn)有函數(shù)對(duì)象創(chuàng)建新函數(shù)對(duì)象的構(gòu)件。因此函數(shù)適配器類(lèi)似于容器適配器。
有多種函數(shù)適配器,包括函數(shù)對(duì)象求反,在函數(shù)對(duì)象的重載函數(shù)調(diào)用操作符中綁定常數(shù)和參數(shù),把函數(shù)指針轉(zhuǎn)換成函數(shù)對(duì)象,以及構(gòu)造現(xiàn)有函數(shù)對(duì)象之外的新函數(shù)對(duì)象
兩種內(nèi)建STL函數(shù)對(duì)象類(lèi)型為unary_function和binary_funciton
函數(shù)對(duì)象unary_function第一個(gè)模板參數(shù)是重載函數(shù)調(diào)用操作符的參數(shù)類(lèi)型此處為unsigned
第二個(gè)模板參數(shù)是操作符返回類(lèi)型,此處為bool
not1(1代表一元)
STL allocator是一個(gè)模板類(lèi),用于內(nèi)存管理。
看一個(gè)大例子:
#include <iostream>
#include <fstream>
#include <deque>
#include <algorithm>
#include <string>
using namespace std;
const string inFile = "stockData.dat";
const string Unknown = "????";
class Stock {
public:
Stock() {
symbol = Unknown;
open = close = gainLoss = volume = 0;
}
Stock(const string &s,double o,double c,unsigned long v) {
symbol = s;
open = o;
close = c;
volume = v;
gainLoss = (close - open) / open;
}
const string & getSymbol() const {
return symbol;
}
double getOpen() const {
return open;
}
double getClose() const {
return close;
}
unsigned long getVolume() const {
return volume;
}
double getGainLoss() const {
return gainLoss;
}
private:
string symbol;
double open;
double close;
double gainLoss;
unsigned long volume;
};
struct winCmp {
bool operator()(const Stock & s1,const Stock & s2) const {
return s1.getGainLoss() > s2.getGainLoss();
}
};
struct volCmp {
bool operator()(const Stock& s1, const Stock & s2) const {
return s1.getVolume() > s2.getVolume() ;
}
};
void output(bool volFlag,
const string& name,
const char * openLabel,double open,
const char * closeLabel,double close,
const char * gainLabel,double gain,
const char * volLabel, unsigned long vol) {
cout << "*** " << name << endl;
if(volFlag)
cout << '\t' << volLabel << vol << endl;
cout << '\t' << gainLabel << gain << endl
<< '\t' << openLabel << open << endl
<< '\t' << closeLabel << close << endl;
if(!volFlag)
cout << '\t' << volLabel << vol << endl;
}
struct winPr {
void operator() (const Stock & s ) const {
output(false,
s.getSymbol(),
"Open Price: ",s.getOpen(),
"Closing Price: ",s.getClose(),
"% Changed: ",s.getGainLoss() * 100,
"Volume: ",s.getVolume() );
}
};
struct volPr {
void operator() (const Stock & s) const {
output( true,
s.getSymbol(),
"Opening Price: ",s.getOpen(),
"Closing Price: ",s.getClose(),
"% Changed: ",s.getGainLoss() * 100,
"Volume: ",s.getVolume() );
}
};
void herald(const char *);
void input(deque<Stock> &);
int main() {
deque<Stock> stocks;
input(stocks);
herald("Gainers in descending order: ");
sort(stocks.begin(),stocks.end(),winCmp());
for_each(stocks.begin(),stocks.end(),winPr());
herald("Loser in asceding order: ");
for_each(stocks.rbegin(),stocks.rend(),winPr());
herald("Volume in descending order: " );
sort(stocks.begin(),stocks.end(),volCmp());
for_each(stocks.begin(),stocks.end(),volPr());
return 0;
}
void input(deque<Stock> & d) {
string s ;
double o,c,v;
ifstream input(inFile.c_str());
while(input >> s >> o >> c>> v)
d.insert(d.end(),Stock(s,o,c,v));
input.close();
}
void herald(const char *s) {
cout << endl << "******* " << s <<endl;
}
結(jié)果:
完整結(jié)果文件輸出:
******* Gainers in descending order:
*** COHU
% Changed: 5.17382
Open Price: 48.9
Closing Price: 51.43
Volume: 134900
*** ALCD
% Changed: 2.46607
Open Price: 60.42
Closing Price: 61.91
Volume: 230000
*** GMGC
% Changed: 1.81159
Open Price: 2.76
Closing Price: 2.81
Volume: 129400
*** MSFT
% Changed: 1.55296
Open Price: 135.87
Closing Price: 137.98
Volume: 8301700
*** ZTEC
% Changed: -0.784016
Open Price: 39.54
Closing Price: 39.23
Volume: 100300
*** TMXI
% Changed: -8.59873
Open Price: 3.14
Closing Price: 2.87
Volume: 255000
*** BUTI
% Changed: -13.8286
Open Price: 8.75
Closing Price: 7.54
Volume: 159000
*** EPEX
% Changed: -17.3342
Open Price: 15.98
Closing Price: 13.21
Volume: 54000
******* Loser in asceding order:
*** EPEX
% Changed: -17.3342
Open Price: 15.98
Closing Price: 13.21
Volume: 54000
*** BUTI
% Changed: -13.8286
Open Price: 8.75
Closing Price: 7.54
Volume: 159000
*** TMXI
% Changed: -8.59873
Open Price: 3.14
Closing Price: 2.87
Volume: 255000
*** ZTEC
% Changed: -0.784016
Open Price: 39.54
Closing Price: 39.23
Volume: 100300
*** MSFT
% Changed: 1.55296
Open Price: 135.87
Closing Price: 137.98
Volume: 8301700
*** GMGC
% Changed: 1.81159
Open Price: 2.76
Closing Price: 2.81
Volume: 129400
*** ALCD
% Changed: 2.46607
Open Price: 60.42
Closing Price: 61.91
Volume: 230000
*** COHU
% Changed: 5.17382
Open Price: 48.9
Closing Price: 51.43
Volume: 134900
******* Volume in descending order:
*** MSFT
Volume: 8301700
% Changed: 1.55296
Opening Price: 135.87
Closing Price: 137.98
*** TMXI
Volume: 255000
% Changed: -8.59873
Opening Price: 3.14
Closing Price: 2.87
*** ALCD
Volume: 230000
% Changed: 2.46607
Opening Price: 60.42
Closing Price: 61.91
*** BUTI
Volume: 159000
% Changed: -13.8286
Opening Price: 8.75
Closing Price: 7.54
*** COHU
Volume: 134900
% Changed: 5.17382
Opening Price: 48.9
Closing Price: 51.43
*** GMGC
Volume: 129400
% Changed: 1.81159
Opening Price: 2.76
Closing Price: 2.81
*** ZTEC
Volume: 100300
% Changed: -0.784016
Opening Price: 39.54
Closing Price: 39.23
*** EPEX
Volume: 54000
% Changed: -17.3342
Opening Price: 15.98
Closing Price: 13.21
模板類(lèi)和繼承:
一個(gè)模板類(lèi)可以從另一個(gè)模板類(lèi)或非模板類(lèi)派生而來(lái),模板類(lèi)或模板類(lèi)實(shí)例都可以作為基類(lèi),而它們的派生類(lèi)既可以是模板類(lèi),也可以使非模板類(lèi)
1>
class B {
// ...
};
template<class T>
class TD : public B {
// ...
};
2>
template<class T>
class TB {
// ...
};
class D : public TB<int> {
// ...
};
template<class T>
class TB {
// ...
};
template<class T>
class D : public TB<T> {
// ...
};
還可以對(duì)STL模板類(lèi)進(jìn)行繼承
有一種落差是,你配不上自己的野心,也辜負(fù)了所受的苦難
總結(jié)
- 上一篇: 5 个免版权高清视频素材下载网站(一)
- 下一篇: Android应用程序App应用上线流程