模板和标准模板库
template<class T>
class Array{
//...
};
T就是類參數,T前面的關鍵字是class或typename。
類參數是個符號,代表通過關鍵字class或typename將要被某個確定類型代替的類型
template和<之間可以有一個空格,但是通常不要有
我們將Array<double>和Array<char>稱為模板類Array<T>的模板實例
不可能存在一個類型為Array或Array<T>的對象,但我們可以定義類型為Array<int>的對象。也就是說,一個對象不能屬于像Array這樣的模板類,但可以屬于Array<char>
這樣的模板類實例
用內建或自定義的數據類型都可以創建模板實例
模板類可以作為一種數據類型出現在參數列表中
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;
}
模板類必須至少有一個類參數,當然可以有多個類參數。模板類還可以有非類參數的參數。一般稱之為函數類型參數(無類型模板參數),一個模板類可以有多個函數類型參數,這些參數的數據類型可以使內建類型或自定義類型參數
template<class T,int x,int y>
class Sample {
};
用具體的數據類型代替模板頭中的類參數,并用具體的數值代替模板頭中的函數類型參數,就可以實例化一個模板類,所以模板類有時稱為參數化的類
模板類中的成員函數可以以內聯函數的形式定義
模板類中的函數類型參數是類參數中除了class或typename定義的類型
例子:
頭文件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;
}
測試文件:
結果:
標準模板庫(Standard Template Library)STL是標準C++庫的一部分。STL的模板類為C++提供了完善的數據結構
容器、算法、和迭代器是STL的三個基本組成部分。
一個STL容器是對象的集合。
STL包括vector、stack、queue、deque、list、set和map等
STL算法是對容器進行處理的函數,例如:copy、sort、search、merge和permute等
STL迭代器是一訪問器中的一種機制,一次訪問一個對象
STL的迭代器可分為 正向、反向、雙向和隨機遍歷
和C++數組不同,STL容器的大小自動變化。
STL基本容器可以分兩組:序列式容器和關聯式容器
例子:vector容器
vector支持兩端插入,并提供begin和end成員函數,分別用來訪問頭部和尾部元素。兩個函數都返回一個可隨機訪問的迭代器
begin:
如果容器不為空,指向容器的第一個元素
如果容器為空,指向容器尾部之后的位置
end:
僅指向容器尾部之后的位置
vector和deque的區別主要在于他們的底層實現不同,特別是插入和刪除操作的實現機制不同。
對于vector來說,不管其大小時多少,在頭部插入的效率總是比在尾部插入的效率低。在尾部插入將耗費固定的時間。在頭部插入時,耗時與vector大小乘正比。
deque與vector不同,不管插入還是刪除操作,也不管這些操作是頭部還是在尾部進行,算法效率是固定的
例子:list容器
list有兩種迭代器:
list<type>::iterator
list<type>::const_iterator
type是我們要定義的類型
vector、deque和list
三者都是STL的基本序列式容器:都有insert和erase成員函數
vector和deque都重載了[]而list沒有,因此我們用list時要使用迭代器
另外效率上也有差別:
STL的關聯式容器:set和map
set是一種集合,其中包含0個或多個不重復的和不排序的元素,這些元素被稱為鍵值
例子:(set)
set重載了而多個insert成員函數,我們既可以向vector那樣采用指定插入位置,也可以不指定
s.insert(s.begin(),66);
s.insert(s.end(),99);
s.insert(33);
不論采用哪種重載方式,insert均能保證在set中不含重復的鍵
find是set中另一個常用的函數用來查看set中是否包含某個指定的鍵值,如果存在返回指向該鍵的迭代器,否則返回end成員函數的值
例子:(map)
map也定義了insert函數,map還重載了[]操作符
容器適配器是基本容器的衍生物,并利用基本容器來實現其特定功能。
容器適配器有三種:stack,queue和priority_queue
stack適配器用于創建LIFO列表。queue適配器用于創建FIFO列表
priority_queue用于創建帶優先級的隊列,其元素以某種優先順序進行刪除
例子:(stack)
默認情況下STL stack衍生自deque因此
stack<char> s;
等價于
stack<char, deque<char> > s;
如果需要讓stack從vector衍生,可采用如下定義方式:
stack<char , vector<char> > s;
stack中的
top函數返回棧頂元素
pop函數刪除棧頂元素
兩個通常配對使用top一個元素判斷后,再pop
例子:(queue)
和stack相同,默認情況下,queue衍生自deque。
queue也有push和pop成員函數
同時提供了front成員函數,用來訪問queue頭元素
例子: (priority_queue)
在默認情況下,priority_queue衍生自vector
優先隊列是指該隊列以某種優先級順序刪除隊列中的元素。例如,一個整數優先級隊列可能以降序方式來刪除元素。這樣就保證了在刪除元素的過程中保持隊列元素的優先級順序
priority_queue使用我們熟悉的push和pop成員函數來插入和刪除。top返回不刪除
我們熟悉的string類以及bitset類也可以做STL容器來使用:
例子:(bitset)
bitset是二進制序列。bitset并不是數學意義上的集合,因為它可以包含重復的數字(畢竟只有0和1)
由于bitset默認構造函數將所有的位都清0,
但是可以用轉型構造函數初始一個bitset的值 bitset(unsigned long n);
bitset<8> bs(9) 就創建一個00001001的bitset
bitset可完成如下功能
1>將某個指定位設置為0或1
2>對某個位或所有位取反
3>測試某個位為0或1
4>進行左移或右移操作
5>進行與、或和異或等標準二進制操作
6>將bitset轉型為string或unsigned long類型
#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;
}
結果:
STL有大量用來處理容器的算法。這些算法可以分為如下幾類:排序和搜索、數值處理、集合運算、復制等
STL算法是用模板函數實現的,如STL的reverse算法:
template<class BidirectionalIterator >
void reverse(BidirectionalIterator it1,BidirectionalIterator it2);
STL算法使用迭代器來變量容器,并在迭代過程中處理容器中的元素:
例子1:
例子2:
STL算法nth_element將序列中的中值元素(median elememt)放置到序列的中間位置。僅僅是找到并放到中間位置,不會對序列排序,結果我們可以看到
nth_element有三個參數,第一個迭代器標志序列頭部,第二個要放置的位置,最后一個迭代器標志序列的尾部
STL的copy算法三個參數,前兩個是迭代器的范圍,最后一個是輸出型迭代器:
輸出迭代器實例:ostream_iterator<char>
表達式ostream_iterator<char>(cout," ")調用帶參數的構造函數來創建迭代器,這個構造函數的第一個參數為輸出流,本來為cout,第二個參數是可選的,用來指定輸出到輸出流的各項之間的分隔符,本例為空格符
輸出迭代器例子:
STL除了上面的還有其他構件:
函數對象(function object)、函數適配器(function adaptor) 和 STL alocator
函數對象是一個類對象,它對函數調用操作符() 進行重載,并且該重載函數是公有的。可用STL的函數對象來代替普通函數
上面的dump函數:
void dump(int i) { cout << i << endl;}
在該程序中將dump作為for_each第三個參數
for_each(v.begin(),v.end(),dump);
作為另一種實現方式,我們可以設計一個函數對象:
template<clasls T>
struct dumpIt {
void operator()(T arg) { cout << arg << endl; }
};
然后調用這個模板類的int型實例的重載調用操作符
for_each(v.begin(),v.end(),dumpIt<int>());
由于在默認的情況下結構的成員是公有的,因此我們使用關鍵字struct來創建dumpIt類。之所以將函數對象的函數調用操作符設計為公有的,是因為要在函數對象的定義之外使用它
一個函數對象的開銷比指針傳遞函數的開銷小,所以函數對象通常比常規函數執行速度更快
函數適配器是用來以現有函數對象創建新函數對象的構件。因此函數適配器類似于容器適配器。
有多種函數適配器,包括函數對象求反,在函數對象的重載函數調用操作符中綁定常數和參數,把函數指針轉換成函數對象,以及構造現有函數對象之外的新函數對象
兩種內建STL函數對象類型為unary_function和binary_funciton
函數對象unary_function第一個模板參數是重載函數調用操作符的參數類型此處為unsigned
第二個模板參數是操作符返回類型,此處為bool
not1(1代表一元)
STL allocator是一個模板類,用于內存管理。
看一個大例子:
#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;
}
結果:
完整結果文件輸出:
******* 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
模板類和繼承:
一個模板類可以從另一個模板類或非模板類派生而來,模板類或模板類實例都可以作為基類,而它們的派生類既可以是模板類,也可以使非模板類
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> {
// ...
};
還可以對STL模板類進行繼承
有一種落差是,你配不上自己的野心,也辜負了所受的苦難
總結
- 上一篇: 5 个免版权高清视频素材下载网站(一)
- 下一篇: Android应用程序App应用上线流程