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