關鍵詞:STL標準模板庫,容器
STL概述
STL(Standard Template Library,標準模板庫)是惠普實驗室開發的一系列軟件的統稱。現然主要出現在C++中,但在被引入C++之前該技術就已經存在了很長的一段時間。從廣義上講分爲三類:algorithm(算法)、container(容器)和iterator(迭代器),容器和算法通過迭代器可以進行無縫地連接。幾乎所有的代碼都採 用了模板類和模板函數的方式,這相比於傳統的由函數和類組成的庫來說提供了更好的代碼重用機會。在C++標準中,STL被組織爲下面的13個頭文 件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>。
STL的六大組件
- 容器(Container)
- 算法(Algorithm)
- 迭代器(Iterator)
- 仿函數(Function object)
- 適配器(Adaptor)
- 空間配製器(allocator)
STL的優點
- STL的一個重要特點是數據結構和算法的分離。
- STL具有高可重用性,高性能,高移植性,跨平臺的優點
容器
所謂容器,經典的數據結構數量有限,但是我們常常重複着一些爲了實現向量、鏈表等結構而編寫的代碼,這些代碼都十分相似,只是爲了適應不同數據的變化而在 細節上有所出入。STL容器就爲我們提供了這樣的方便,它允許我們重複利用已有的實現構造自己的特定類型下的數據結構,通過設置一些模板,STL容器對最常用的數據結構提供了支持,這些模板的參數允許我們指定容器中元素的數據類型,可以將我們許多重複而乏味的工作簡化。
容器的分類:
- 序列式容器(Sequence containers)
每個元素都有固定位置--取決於插入時機和地點,和元素值無關。
vector、deque、list
- 關聯式容器(Associated containers)
元素位置取決於特定的排序準則,和插入順序無關
set、multiset、map、multimap
迭代器:
迭代器從作用上來說是最基本的部分,可是理解起來比前兩者都要費力一些。軟件設計有一個基本原則,所有的問題都可以通過引進一個間接層來簡化, 這種簡化在STL中就是用迭代器來完成的。概括來說,迭代器在STL中用來將算法和容器聯繫起來,起着一種黏和劑的作用。幾乎STL提供的所有算法都是通 過迭代器存取元素序列進行工作的,每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。
- 迭代器是一個“可遍歷STL容器內全部或部分元素”的對象。
- 迭代器指出容器中的一個特定位置。
- 迭代器就如同一個指針。
- 迭代器提供對一個容器中的對象的訪問方法,並且可以定義了容器中對象的範圍。
這裏大概介紹一下迭代器的類別。
- 輸入迭代器:也有叫法稱之爲“只讀迭代器”,它從容器中讀取元素,只能一次讀入一個元素向前移動,只支持一遍算法,同一個輸入迭代器不能兩遍遍歷一個序列。
- 輸出迭代器:也有叫法稱之爲“只寫迭代器”,它往容器中寫入元素,只能一次寫入一個元素向前移動,只支持一遍算法,同一個輸出迭代器不能兩遍遍歷一個序列。
- 正向迭代器:組合輸入迭代器和輸出迭代器的功能,還可以多次解析一個迭代器指定的位置,可以對一個值進行多次讀/寫。
- 雙向迭代器:組合正向迭代器的功能,還可以通過--操作符向後移動位置。
- 隨機訪問迭代器:組合雙向迭代器的功能,還可以向前向後跳過任意個位置,可以直接訪問容器中任何位置的元素。
C++標準庫:
C++強大的功能來源於其豐富的類庫及庫函數資源。C++標準庫的內容總共在50個標準頭文件中定義。在C++開發中,要儘可能地利用標準庫完 成。這樣做的直接好處包括:
(1)成本:已經作爲標準提供,何苦再花費時間、人力重新開發呢;
(2)質量:標準庫的都是經過嚴格測試的,正確性有保證;
(3)效率:關於人的效率已經體現在成本中了,關於代碼的執行效率要相信實現標準庫的大牛們的水平;
(4)良好的編程風格:採用行業中普遍的做法進行開發。
幾個主要容器
string
string是STL的字符串類型,通常用來表示字符串。
/*爲了方便整理,把STL中與String相關的內容分成各個函數*/
//string 的構造函數
void StringInit()
{
string s1; //無參的默認構造函數
string s2("tqn666"); //有參的構造函數
string s3(6, 'w'); //構造N個字符
string s4(s2); //拷貝構造函數
}
/****************************************************************************/
//String中取字符操作
void StringAt()
{
string s1("tqn666");
cout << s1[3] << endl; //用[]取第四個字符‘6’
cout << s1.at(3) << endl; //用at()函數取第四個字符‘6’
try
{
cout << s1.at(7) << endl; //at()與[]的區別就是at()更安全,當越界時能拋出異常
}
catch (exception &e)
{
cout << e.what() << endl;
}
}
/****************************************************************************/
//string與const char *的轉換
void StringStr()
{
string s1("tqn666");
const char *ptr = s1.c_str(); //使用自帶的c_str()函數
cout << ptr << endl;
}
/****************************************************************************/
//string的拷貝操作
void StringCopy()
{
string s1("tqn666");
char *buf = new char[32];
s1.copy(buf, 3); //直接使用copy()函數,參數依次是目標,長度,從第幾個開始拷貝
cout << buf << endl;
}
/****************************************************************************/
//string的求字符串長度,判空操作
void StringLength()
{
string s1("helloworld");
cout << s1.length() << endl;
string s2;
if (s2.empty())
{
cout << "empty" << endl;
}
}
/****************************************************************************/
//string的賦值
void StringAssign()
{
string s1("12345678");
string s2;
s2 = s1;
cout << s2 << endl;
string s3;
s3.assign("12345");
cout << s3 << endl;
s3.assign("134556", 3);
cout << s3 << endl;
s3.assign(10, 'a');
cout << s3 << endl;
s3.assign(s2);
cout << s3 << endl;
s3.assign(s1, 3, 3);
cout << s3 << endl;
}
/****************************************************************************/
//string的字符串連接
void StringAppend()
{
string s1("aaa");
s1 += "bbb";
cout << s1 << endl;
string s2("bbb");
s2.append(s1);
cout << s2 << endl;
s2.append(10, 'x');
cout << s2 << endl;
s2.append(s1, 4, 2);
cout << s2 << endl;
}
/****************************************************************************/
//字符串比較
void StringCompare()
{
string s1("hello");
string s2("world");
if (s1.compare(s2) < 0)
{
cout << "小於" << endl;
}
if (s2.compare("aaaaa") > 0)
{
cout << "大於" << endl;
}
}
/****************************************************************************/
//求子串
void StringSub()
{
string s1("helloworld");
cout << s1.substr(3, 4) << endl;
}
/****************************************************************************/
//查找
void StringFind()
{
string s1("helloworldhelloworldhelloworld");
int index = s1.find('x');
cout << index << endl;
cout << s1.find('x') << endl;;
index = s1.find('w', 6);
cout << index << endl;
index = s1.find("world", 1);
cout << index << endl;
//s1.replace(5, 5, "xx");
//cout << s1 << endl;
index = s1.find("world", 0);
while (index != -1)
{
s1.replace(index, 5, "xx");
index = s1.find("world", index + strlen("xx"));
}
cout << s1 << endl;
}
/****************************************************************************/
//插入
void StringInsert()
{
string s1("helloworld");
s1.insert(5, "xxxx");
cout << s1 << endl;
s1.erase(5, 4);
cout << s1 << endl;
}
string中at()與[ ]的區別
vector
vector是將元素置於一個動態數組中加以管理的容器。
vector可以隨機存取元素(支持索引值直接存取, 用[]操作符或at()方法)。
vector尾部添加或移除元素非常快速。但是在中部或頭部插入元素或移除元素比較費時
//vector的構造
int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
vector<int> v1;
vector<int> v2(10);
vector<int> v3(array, array + sizeof(array) / sizeof(array[0]));
vector<int> v4(10, 10);
/***********************************************************************/
//vector的賦值
void VectorAssign()
{
int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
vector<int> v1(10);
v1.assign(array, array + sizeof(array) / sizeof(array[0]));
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
vector<int> v2(10);
v2.assign(10, 10);
for (int i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
v2 = v1;
for (int i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
vector<int> v3(10);
for (int i = 0; i < v3.size(); i++)
{
v3[i] = i + 1;
}
v2 = v3;
for (int i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
v3.swap(v1);
for (int i = 0; i < v3.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
}
/***********************************************************************/
//vector的數據存取,通過迭代器
void VectorIterator()
{
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i + 1;
}
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
for (vector<int>::reverse_iterator rit = v1.rbegin(); rit != v1.rend(); rit++)
{
cout << *rit << " ";
}
cout << endl;
}
/***********************************************************************/
//vector的插入
void VectorInsert()
{
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i + 1;
}
vector<int>::iterator it = v1.begin();
it += 3;
v1.insert(it, 100);
for (it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
it = v1.begin();
it++;
v1.insert(it, 10, 20);
for (it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
vector<int> v2(10);
for (int i = 0; i < v2.size(); i++)
{
v2[i] = i + 20;
}
it = v2.begin();
v1.insert(v1.begin(), it, it + 5);
for (it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
v2.push_back(1000);
for (it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
v2.pop_back();
for (it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
/***********************************************************************/
//vector的大小
void VectorSize()
{
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i + 1;
}
v1.resize(20, 99);
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
v1.resize(5);
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
/***********************************************************************/
//vector的刪除
void VectorErase()
{
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i + 1;
}
vector<int>::iterator it = v1.begin();
v1.erase(it + 1, it + 8);
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
v1.clear();
}
deque
deque是“double-ended queue”的縮寫,和vector一樣都是STL的容器,deque是雙端數組,而vector是單端的。
deque在接口上和vector非常相似,在許多操作的地方可以直接替換。
deque可以隨機存取元素(支持索引值直接存取, 用[]操作符或at()方法,這個等下會詳講)。
deque頭部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比較費時。
deque與vector用法一致,效率更高,這裏不再贅述
priority_queue
最大值優先級隊列、最小值優先級隊列,優先級隊列適配器 STL priority_queue
#include "iostream"
#include "time.h"
#include "queue"
#include "functional"
#include "cstring"
using namespace std;
class Student
{
friend class Compare;
public:
int m_id;
Student(int id);
void print()const;
};
Student::Student(int id)
{
m_id = id;
}
void Student::print()const
{
cout<<m_id<<endl;
}
class Compare
{
public:
bool operator ()(const Student &s1, const Student &s2)
{
return (s1.m_id > s2.m_id);
}
};
int main()
{
srand(time(NULL));
//priority_queue<int, deque<int>, less<int> >q;
/*priority_queue<int, deque<int>, greater<Student> >q;
for (int i = 0; i < 10; i++)
{
q.push(rand()%20);
}
for (int i = 0; i < 10; i++)
{
cout << q.top() << " ";
q.pop();
}*/
Student s1(rand() % 20);
Student s2(rand() % 20);
Student s3(rand() % 20);
priority_queue<Student, deque<Student>, Compare >q1;
q1.push(s1);
q1.push(s2);
q1.push(s3);
int length = q1.size();
for (int i = 0; i < length; i++)
{
q1.top().print();
q1.pop();
}
system("pause");
return 0;
}
最大優先級或最小優先級由最後一個參數決定,這是一個庫裏封裝好的函數對象,less最大優先,greater最小優先,也可以自定義一個函數對象
set/multiset
- set是一個集合容器,其中所包含的元素是唯一的,集合中的元素按一定的順序排列。元素插入過程是按排序規則插入,所以不能指定插入位置。
- set採用紅黑樹變體的數據結構實現,紅黑樹屬於平衡二叉樹。在插入操作和刪除操作上比vector快。
- set不可以直接存取元素。(不可以使用at.(pos)與[]操作符)。
- multiset與set的區別:set支持唯一鍵值,每個元素值只能出現一次;而multiset中同一值可以出現多次。
- 不可以直接修改set或multiset容器中的元素值,因爲該類容器是自動排序的。如果希望修改一個元素值,必須先刪除原有的元素,再插入新的元素。
set的默認構造
set<int> setInt; //一個存放int的set容器。
set<float> setFloat; //一個存放float的set容器。
set<string> setString; //一個存放string的set容器。
multiset<int> mulsetInt; //一個存放int的multi set容器。
multi set<float> multisetFloat; //一個存放float的multi set容器。
multi set<string> multisetString; //一個存放string的multi set容器。
set的插入與迭代器
set.insert(elem); //在容器中插入元素。
set.begin(); //返回容器中第一個數據的迭代器。
set.end(); //返回容器中最後一個數據之後的迭代器。
set.rbegin(); //返回容器中倒數第一個元素的迭代器。
set.rend(); //返回容器中倒數最後一個元素的後面的迭代器。
set<int> setInt;
setInt.insert(3); setInt.insert(1);setInt.insert(5);setInt.insert(2);
for(set<int>::iterator it=setInt.begin(); it!=setInt.end(); ++it)
{
int iItem = *it;
cout << iItem; //或直接使用cout << *it
}
//這樣子便順序輸出 1 2 3 5。
set的元素排序
set<int,less<int> > setIntA; //該容器是按升序方式排列元素。
set<int,greater<int>> setIntB; //該容器是按降序方式排列元素。
函數對象functor
- 儘管函數指針被廣泛用於實現函數回調,但C++還提供了一個重要的實現回調函數的方法,那就是函數對象。
- functor,翻譯成函數對象,僞函數,算符,是重載了“()”操作符的普通類對象。從語法上講,它與普通函數行爲類似。
- greater<>與less<>就是函數對象
set的 拷貝構造與賦值,大小,刪除,查找
set(const set &st); //拷貝構造函數
set& operator=(const set &st); //重載等號操作符
set.swap(st); //交換兩個集合容器
set.size(); //返回容器中元素的數目
set.empty();//判斷容器是否爲空
set.clear(); //清除所有元素
set.erase(pos); //刪除pos迭代器所指的元素,返回下一個元素的迭代器。
set.erase(beg,end); //刪除區間[beg,end)的所有元素 ,返回下一個元素的迭代器。
set.erase(elem); //刪除容器中值爲elem的元素。
set.find(elem); //查找elem元素,返回指向elem元素的迭代器。
set.count(elem); //返回容器中值爲elem的元素個數。對set來說,要麼是0,要麼是1。對multiset來說,值可能大於1。
set.lower_bound(elem); //返回第一個>=elem元素的迭代器。
set.upper_bound(elem); // 返回第一個>elem元素的迭代器。
set.equal_range(elem); //返回容器中與elem相等的上下限的兩個迭代器。上限是閉區間,下限是開區間,如[beg,end)。
multiset與set的區別:set支持唯一鍵值,每個元素值只能出現一次;而multiset中同一值可以出現多次。其他用法一致。
map/multimap
- map是標準的關聯式容器,一個map是一個鍵值對序列,即(key,value)對。它提供基於key的快速檢索能力。
- map中key值是唯一的。集合中的元素按一定的順序排列。元素插入過程是按排序規則插入,所以不能指定插入位置。
- map的具體實現採用紅黑樹變體的平衡二叉樹的數據結構。在插入操作和刪除操作上比vector快。
- map可以直接存取key所對應的value,支持[]操作符,如map[key]=value。
- multimap與map的區別:map支持唯一鍵值,每個鍵只能出現一次;而multimap中相同鍵可以出現多次。multimap不支持[]操作符。
map封裝好了,其各功能函數與set類似,這裏不再贅述
最後說幾點
- 所有容器提供的都是值(value)語意,而非引用(reference)語意。容器執行插入元素的操作時,內部實施拷貝動作。所以STL容器內存儲的元素必須能夠被拷貝(必須提供拷貝構造函數)。
- 除了queue與stack外,每個容器都提供可返回迭代器的函數,運用返回的迭代器就可以訪問元素。
- 通常STL不會丟出異常。要求使用者確保傳入正確的參數。
- 每個容器都提供了一個默認構造函數跟一個默認拷貝構造函數。