C++ STL(標(biāo)準(zhǔn)模板庫(kù))是一套功能強(qiáng)大的c++模板類提供了通用的模板類和函數(shù),這些模板類和函數(shù)可以實(shí)現(xiàn)多種流行和常用的算法和數(shù)據(jù)結(jié)構(gòu),如向量、鏈表、隊(duì)列、棧。包含以下三個(gè)組成部分
組件 描述 容器(Containers) 容器是一個(gè)與數(shù)組類似的單元,用來(lái)存儲(chǔ)值,且存儲(chǔ)的值的類型相同;比如deque、list、vector、map等 算法(Algorithms) 算法作用于容器,它們提供了執(zhí)行各種操作的方法,包括對(duì)容器內(nèi)容執(zhí)行初始化、排序、搜索、轉(zhuǎn)換等操作 迭代器(iterators) 迭代用于遍歷對(duì)象集合的元素,這些集合可能是容器,也可能是容器的子集
零、 前言
1. 命名空間
當(dāng)工程代碼采用不同的程序和程序庫(kù)時(shí),針對(duì)不同的對(duì)象使用相同的標(biāo)識(shí)符,就會(huì)出現(xiàn)名稱沖突的現(xiàn)象,使用namespace就可以解決這個(gè)問題。標(biāo)識(shí)符的可見范圍namespace和class不同,namespace具有擴(kuò)展開放性,可以出現(xiàn)在任意源碼文件中。
C++ 標(biāo)準(zhǔn)程序庫(kù)的所有標(biāo)識(shí)符都被定義于一個(gè)名為std的namespace中,有以下三種方法使用:
直接指定標(biāo)識(shí)符,例如
std::cout << std::hex << 3.4 << std::endl;
使用using declaration的方法,例如:
using std::cout;
using std::endl;
使用using directive,這是最簡(jiǎn)便的方法,就像被聲明為全局標(biāo)識(shí)符一樣,但是由于某些重載的規(guī)格,這種方法可能導(dǎo)致意外地命名沖突,因此應(yīng)避免使用第三種,除非程序很小為了方便。
using namespace std;
cout << hex << 3.4 << endl;
2. 通用工具
Pairs(對(duì)組)
class pair,凡需要將兩個(gè)值視為一個(gè)單元的場(chǎng)景(例如必須返回兩個(gè)值的某函數(shù))就必須用到它,例如容器類別map和multimap,就是使用pairs來(lái)管理鍵值對(duì)元素
std::pair<int, float> p; // 初始化p.first 和 p.second為 0
std::pair<int, char *> p(42, "hello");
數(shù)值極限
標(biāo)準(zhǔn)庫(kù)通過(guò)template numeric_limits提供極值,定義于,浮點(diǎn)數(shù)定義于
#include<iostream>
#include<string>
#include<limits> //頭文件
using namespace std;
int main(){
cout<<"numeric_limits<unsigned short>::min()= "<<numeric_limits<unsigned short>::min()<<endl; //unsigned short的最小值
cout<<"numeric_limits<unsigned short>::max()= "<<numeric_limits<unsigned short>::max()<<endl; //unsigned short的最大值
cout<<"numeric_limits<int>::min()= "<<numeric_limits<int>::min()<<endl; //int的最小值
cout<<"numeric_limits<int>::max()= "<<numeric_limits<int>::max()<<endl; //int的最大值
cout<<"numeric_limits<short>::min()= "<<numeric_limits<short>::min()<<endl;
cout<<"numeric_limits<short>::max()= "<<numeric_limits<short>::max()<<endl;
cout<<"numeric_limits<double>::min()= "<<numeric_limits<double>::min()<<endl;
cout<<"numeric_limits<double>::lower()="<<numeric_limits<double>::lower()<<endl; //double最小值是lower,min只會(huì)返回e的負(fù)數(shù)次方
cout<<"numeric_limits<double>::max()= "<<numeric_limits<double>::max()<<endl;
cout<<"numeric_limits<int>::is_signed()= "<<numeric_limits<int>::is_signed<<endl;//是否有正負(fù)號(hào)
cout<<"numeric_limits<string>::is_specialized()= "<<numeric_limits<string>::is_specialized<<endl;//是否定義了數(shù)值極限
return 0;
}
輔助函數(shù)
定義在內(nèi)的是哪個(gè)輔助函數(shù),max()、min()、swap()。
namespace std {
template <class T>
inline const T& min(const T& a, const T& b) {
return b < a ? b : a;
}
template <class T>
inline const T& max(const T& a, const T& b) {
return a < b ? b : a;
}
template <class T>
inline void swap(T& a, T& b) {
T temp {a};
a = b;
b = temp;
}
}
非成員函數(shù)的方法
如果要為每一個(gè)容器類型,單獨(dú)定義一個(gè)排序,查找的方法,則工作量會(huì)非常巨大,并且很多都是重復(fù)性的工作,因此STL定義了非成員函數(shù),來(lái)完成通用的容器算法,如sort(),find(),swap().如果該容器類型vector.swap()存在,則代表要比普通的swap()函數(shù)效率更高.
for ( vector< int > :: iterator pr = test. begin ( ) ; pr != test. end ( ) ; pr++ )
替換為:
for_each ( test. begin ( ) , test. end ( ) , cout) ;
sort ( test. begin ( ) , test. end ( ) ) ;
1 > 如果要排序的對(duì)象是用戶自己定義的, 則需要重載比較操作符
struct Review {
string title;
int rating;
}
bool operator< ( const Review & r1, const Review & r2)
{
if ( r1. title < r2. title) {
return true;
} else if ( ri. title == r2. title && r1. rating < r2. rating) {
return true;
} else {
return false;
}
}
然后就可以使用sort函數(shù)對(duì)自定義對(duì)象進(jìn)行排序了.
sort ( book. begin ( ) , book. end ( ) ) ;
2 > 如果想按照其他排序方式, 則可以使用第三個(gè)入?yún)?bool worseTahn ( const Review & r1, const Review & r2)
{
if ( r1. rating < r2. rating) {
return true;
} esle {
return false;
}
}
3.capacity和size屬性的區(qū)別:
size : 是當(dāng)前vector容器真實(shí)占用的大小,也就是容器擁有多少個(gè)元素,對(duì)應(yīng)resize()
capacity : 是值發(fā)生在realloc前能允許的最大元素?cái)?shù),即預(yù)分配的內(nèi)存空間,對(duì)應(yīng)reserve(),對(duì)于list, map, set, deque由于內(nèi)存是散列分布的,因此capacity()對(duì)于這些容器是沒有意義的.在STL中,擁有capacity屬性的只有vector和string.
# include <iostream>
# include <vector>
using std:: vector;
int main ( void )
{
vector< int > v;
std:: cout<< "v.size() == " << v. size ( ) << " v.capacity() = " << v. capacity ( ) << std:: endl;
v. reserve ( 10 ) ;
std:: cout<< "v.size() == " << v. size ( ) << " v.capacity() = " << v. capacity ( ) << std:: endl;
v. resize ( 10 ) ;
v. push_back ( 0 ) ;
std:: cout<< "v.size() == " << v. size ( ) << " v.capacity() = " << v. capacity ( ) << std:: endl;
return 0 ;
}
對(duì)于空的vector,如果直接用[]訪問,可能會(huì)發(fā)生越界報(bào)錯(cuò),建議用at()會(huì)首先進(jìn)行越界檢查.
3. 容器類型
總體容器可以分為兩類:
序列式容器Sequence containers:表述為可序的群集,每個(gè)元素均有固定的位置,取決于插入時(shí)機(jī)和地點(diǎn),和元素值無(wú)關(guān),如vector, deque, list。 關(guān)聯(lián)式容器Associative containers:表述為已序群集,元素位置取決于特定的排序準(zhǔn)則,如果元素的位置取決于元素值,和插入次序無(wú)關(guān),例如:set, multiset, map, multimap。 關(guān)聯(lián)式容器在C++實(shí)現(xiàn)中,采用紅黑樹的方法,因此會(huì)自動(dòng)對(duì)元素排序,其優(yōu)點(diǎn)為當(dāng)搜索元素時(shí),可以放心的使用二分搜索法等有序要求的算法。
一、String容器
當(dāng)程序需要處理字符串的時(shí)候,C語(yǔ)言在string.h和cstring里提供了一系列函數(shù),但是不支持C++的string類。string類也是STL容器中的一種
1. string類構(gòu)造函數(shù)
# 1. 按照C風(fēng)格創(chuàng)建字符串
string one ( "hello world" ) ;
# 2. 創(chuàng)建由10 個(gè)C組成的字符串
string two ( 10 , 'c' ) ; // "cccccccccc"
# 3. 復(fù)制構(gòu)造函數(shù)將string對(duì)象初始化為對(duì)象one
string three ( one) ; // "hello world"
# 4. 重載[ ] 運(yùn)算符,可以用下標(biāo)訪問
three[ 0 ] = 'P'
# 5. 重載+= 運(yùn)算符將字符串Oops附件到字符串one后
one += "Oops!" ;
string four; // 默認(rèn)構(gòu)造函數(shù)創(chuàng)建一個(gè)以后可以賦空值
four = two + three + '!' // 重載=運(yùn)算符,可以給對(duì)象賦值
# 6. 將C風(fēng)格字符串和整數(shù)作為參數(shù),其中整數(shù)表示要復(fù)制多少個(gè)字符串
char alls[ ] = "All well that end well"
string five ( alls, 20 )
# 7. 迭代器模板拷貝,[ begin, end) 是迭代器,前閉后開
template< class Iter> string ( Iter begin, Iter end) ;
string six ( alls + 6 , alls + 10 ) ;
string six ( five + 6 , five + 10 ) ; //這種方法不可行,因?yàn)閒ive是對(duì)象,并不是指針
# 8. 將另一個(gè)string對(duì)象的部分內(nèi)容拷貝到構(gòu)造的對(duì)象中
string seven ( four, 7 , 16 ) ;
2. string類輸入
# 1. 按照C風(fēng)格輸入字符串
char info[ 100 ] ;
cin >> info; // 讀取一個(gè)單詞
cin. getline ( info, 100 ) ; // 讀取一行,丟棄換行符
cin. get ( info, 100 ) ; // 讀取一行,換行符保存在隊(duì)列中
# 2. 對(duì)于string對(duì)象,有兩種方式:使用cin讀入字符串時(shí),遇到空白就停止讀取。比如程序輸入的是
" Hello World"
那么我們得到的字符串將是"Hello" ,前面的空白沒了,后面的world也讀不出來(lái)。如果我們想把整個(gè)hello world讀進(jìn)來(lái)怎么辦?那就這樣做
cin >> s1 >> s2;
hello存在s1里,world存在s2里了。有時(shí)我們想把一個(gè)句子存下來(lái),又不想像上面那樣創(chuàng)建多個(gè)string來(lái)存儲(chǔ)單詞,怎么辦?那就是用getline來(lái)獲取一整行內(nèi)容。
string str;
getline ( cin, str) ; // 會(huì)自動(dòng)調(diào)整string的大小,使其剛好存下輸入
cout << str << endl;
3. string類方法
# 1. 重載了比較操作符, '<' , '>' , '='
string one ( "gedit" ) ;
string two ( "name" ) ;
one > two || one < two || one != two
# 2. 判斷字符串長(zhǎng)度
one. size ( ) == one. length ( ) ; // size和length的函數(shù)行為一模一樣,size是為了提供stl兼容性而后添加的
# 3.f ind方法
one. find ( 'e' ) ; // 返回字符串中第一次出現(xiàn)e的下標(biāo)
one. find ( "dit" ) ; // 返回字符串中第一次出現(xiàn)子串的下標(biāo)
one. rfind ( 'e' ) ; // 返回字符串中最后一次出現(xiàn)e的下標(biāo)
one. find_first_of ( "eat" ) ; // 返回"eat"中任意一個(gè)字符最早出現(xiàn)的索引
one. find_last_of ( "eat" ) // 返回"eat"中任意一個(gè)字符最后出現(xiàn)的索引
one. find_first_not_of ( "eat" ) // 返回原字符串,在"eat"中第一個(gè)沒有出現(xiàn)的索引
# 4. 內(nèi)存塊大小
程序?qū)⒁粋€(gè)字符添加到字符串的末尾時(shí),不能僅僅將已有的字符串加大,相鄰的內(nèi)存可能被占用了,因此可能需要分配一個(gè)新的內(nèi)存塊,并將原來(lái)的信息拷貝過(guò)去。
實(shí)現(xiàn)過(guò)程:
1 > 初始創(chuàng)建的時(shí)候,分配一個(gè)比實(shí)際字符串大的內(nèi)存塊。
2 > 如果超過(guò)了內(nèi)存塊的大小,程序?qū)⒎峙湟粋€(gè)原來(lái)為兩倍的新內(nèi)存空間。
3 > 方法capacity ( ) 返回當(dāng)前分配給字符串的內(nèi)存塊大小,reserve ( ) 獲取內(nèi)存塊的最小長(zhǎng)度
二、vector容器
vector表示了一組可以隨機(jī)訪問的值,可以通過(guò)[]運(yùn)算符來(lái)直接訪問矢量的第n個(gè)元素.
1. vector分配器
通常使用來(lái)表示使用的類型,同時(shí)vector是動(dòng)態(tài)分配的內(nèi)存
int n;
cin >> n;
vector< int > test ( n) ;
分配器可以在構(gòu)造函數(shù)摸板的時(shí)候選擇,該參數(shù)指定了使用哪個(gè)分配器來(lái)管理內(nèi)存
template< class T, class Allocator = allocator< T> >
如果省略了Allocator的值, 則默認(rèn)使用new和delete來(lái)管理內(nèi)存.
2. vector方法
所有的STL容器都提供了一些基礎(chǔ)方法,其中包括了size(),swap(),begin(),end()等.
# 1. 迭代器
vector< double > scores;
vector< double > :: iterator pd;
pd = score. begin ( )
for ( pd = score. begin ( ) ; pd != score. end ( ) ; pd++ ) { // 這里不能用pd<sorce.end()
* pd = 12.3 ;
cout << pd[ i] ;
}
# 2. 從尾部添加元素, 自動(dòng)負(fù)責(zé)內(nèi)存管理
vector< int > test;
while ( cin >> temp && temp >= 0 ) {
test. push_back ( temp) ;
}
# 3. 刪除區(qū)間的元素, 左閉右開
test. erase ( test. begin ( ) , test. begin ( ) + 2 ) ;
# 4. 在某位置插入元素, 或插入一個(gè)區(qū)間
test. insert ( test. begin ( ) , 1 ) ;
test. insert ( test. begin ( ) , new. begin ( ) , new. end ( ) )
# 5. 插入和彈出從尾部的效率是O ( 1 ) , 其他從任意位置都是O ( n)
彈出的過(guò)程不能獲得元素, 因此需要先獲取, 再?gòu)棾?test. pop_back ( )
test. push_back ( )