C++標準模板庫STL深入講解
認識STL
STL的概述
STL采用泛型思想,把C中所用到的所有的數(shù)據(jù)結構,按照一定的標準,全部封裝成了一個個類模板。也被稱為數(shù)據(jù)容器。
STL就是用來解決容器中數(shù)據(jù)元素的操作的問題的。并且他按排標準統(tǒng)一封裝了操作容器元素的算法,即一個個的函數(shù)模板。
為了配合統(tǒng)一的算法去操作不同的容器,他又按標準統(tǒng)一封裝了不同的迭代器,即一個個不同類型的具有指針特性的類模板。
所以容器,算法,迭代器是整個STL的核心。
三者的關系如圖所示:

有了統(tǒng)一的數(shù)據(jù)結構,即容器,有了統(tǒng)一的算法,每一個容器都使用各自的不同的迭代器,從而實現(xiàn)了對數(shù)據(jù)結構元素的標準操作。
STL標準模板庫都有什么
STL的知識結構如圖:

容器
- 常用的數(shù)據(jù)結構,C++重新封裝成了 vector(動態(tài)數(shù)組),list(鏈表),deque(雙向隊列),set(集合) ,map(映射表)等,來實現(xiàn)存放數(shù)據(jù),其中 set,map雙叫關聯(lián)容器 。其中的棧,單端隊列,優(yōu)先隊列,又都是線性數(shù)據(jù)結構改造之后的具有各種特性的數(shù)據(jù)結構,又叫做容器適配器。
- 序列式容器強調值的排序,序列式容器中的每個元素均有固定的位置,除非用刪除或插入的操作改變這個位置。vector容器,deque容器,List容器等。
- 關聯(lián)式容器是非線性的樹結構,更準確的說是二叉樹結構,各元素之間沒有嚴格的物理上的順序關系,也就是說元素在容器中并沒有保存元素入容器時的邏輯順序 。關聯(lián)式容器另一個顯著特點是:在值中選擇一個值作為關鍵字Key,這個關鍵字對值起到了索引的作用,方便查找。Set/mulitiset容器 Map/multimap容器,并且容器是可以嵌套使用的。
算法
algorithom頭文件中的定義相關的操作的一系列的函數(shù)模板
STL收錄的算法經過了數(shù)學上的效能分析與證明,且是經過商業(yè)運行考驗過的,是極具復用價值 的,包括常用的排序,查找等。
? 算法又可為為兩種,質變算法,與非質變算法。
? 何為質變算法:是指運算過程中會更改區(qū)間內的元素的內容。例如:拷貝,替換,刪除等。
? 何為非質變算法:是指運算過程中不會更改區(qū)間內的元素內容,例如:查找,計數(shù),遍歷,尋找極值等。
迭代器
迭代器的設計思維-STL 的關鍵所在,STL 的中心思想在于將容器(container)和算法(algorithms)分開,彼此獨立設計,最后再一貼膠著劑將他們撮合在一起。從技術角度來看,容器和算法的泛型化并不困難,c++的 class template 和 functiontemplate 可分別達到目標,如果設計出兩這個之間的良好的膠著劑,才是大難題
提供一種方法,使之能夠依序尋訪某個容器所含的各個元素,而又無需暴露該容器的內部表示方式,迭代器就被發(fā)明了出來。

函數(shù)符
在STL中主要用來為泛型算法提供策略。
有四種形式:全局函數(shù)指針,成員函數(shù)指針,仿函數(shù)(函數(shù)對象),與匿名函數(shù)Lambda表達式。
空間配置器
作用:主是用來解決內存碎片化問題的,以及過多的構造無用對象的問題。
容器的空間配置器的作用是把對象的內存開辟和構造過程分開,對象析構和內存釋放分離開 。那為什么要把對象的內存開辟和構造(new的兩個作用)分開,對象的析構和內存釋放(delete的兩個作用)分開呢?
1)因為在使用容器的過程中,構造一個容器,我們只想要容器的內存空間,并不需要給我們在內存上構造一堆無用的對象出來(直接用new沒有辦法避免這個問題);當從容器中刪除一個元素的時候,我們需要析構這個被刪除對象,但是它占用的容器內存空間是不能釋放的,因為容器還要使用;再比如我們需要釋放一個容器的時候,需要先把容器里面有效對象元素析構一遍,而不是把容器所有元素都析構一遍(用delete無法避免這個問題),所以在操作容器的過程中,我們需要有選擇性的分別去開辟內存,構造對象,析構對象,所以這就是空間配置器存在的意義。
C++ 標準STL里面提供的allocator空間配置器,默認的內存分配和釋放就是依賴malloc和free實現(xiàn)的。SGI STL提供了兩個版本的空間配置器,一級空間配置器和二級空間配置器。一級空間配置器的內存管理也是通過malloc和free實現(xiàn)的,但是SGI STL的二級空間配置器提供了一個內存池的實現(xiàn)。第二級配置器的設計思想為:
1.如果配置區(qū)塊超過128 bytes,則移交給第一級配置器處理(空間配置器);
2.如果配置區(qū)塊小于128 bytes,則采用內存池管理(memory pool)。每次配置一大塊內存,則維護對應的自由鏈表(free-list),下次若再有相同大小的內存需求,就直接從 free-list 中撥出(沒有就繼續(xù)配置內存,具體后面講述),如果客端釋換小額區(qū)塊,就有配置器回收到 free-list 中。
對于SGI STL的二級空間配置器的內存池實現(xiàn)機制,還是非常重要的,因為這既涉及了空間配置器,又涉及了一個內存池的實現(xiàn)機制,因此大家需要好好的理解它的原理,大家在手寫C++空間配置器的過程中,使用過nginx的內存池作為空間配置器的內存管理方案,這又是一個新的積累。
string字符容器庫
字符串容器API接口:
#include <iostream>
using namespace std;
int main()
{
string str(15,'g');
string str10;
str10.assign(10,'k');
cout << str << endl;
string str1(str,5);
cout << str1 << endl;
string str2("yaoliang",8);
cout << str2 << endl;
const char* s1=str2.data();
const char* s=str2.c_str();
cout << s << endl;
//獲取迭代器
string::iterator it;
for(it=str2.begin();it!=str2.end();it++){
cout << *it << " ";
}
cout << endl;
//獲取反向迭代器
string::reverse_iterator it1;
for(it1=str2.rbegin();it1!=str2.rend();it1++){
cout << *it1 << " ";
}
cout << endl;
cout << str2.size() << endl;
cout << str2.length() << endl;
cout << str2.capacity() << endl;//已經開辟好的空間
cout << str2.max_size() << endl;//最大容量
//string 容量對象保存字符時,如果是小字符串(小于23個字節(jié)),會在棧上開辟空間
//如果是大對象(大于23個字節(jié)),會在堆上開辟空間
//查看動態(tài)開辟空間策略(2倍擴容)
// string str_test;
// cout << str_test.capacity() << endl;
// for(int i=0;i<1024;i++){
// cout << "string容器對象中的有效元素個數(shù):" << str_test.size() <<
// ",string容器對象中的容量大小:" << str_test.capacity() << endl;//采取2倍擴容機制
// str_test.push_back('a');
// }
cout << str1.insert(0,3,'A') << endl;
cout << str1 << endl;
cout << str1.insert(0,"hello") << endl;
cout << str1.erase(0,6) << endl;
cout << str2.find("liang") << endl;
cout << str2.substr(3,5) << endl;
cout << str2.replace(3,5,"ming");
return 0;
}
vector容器
vector容器于array數(shù)組容器的區(qū)別
vector與array無乎是一樣的,連續(xù)的存儲結構,兩者的唯一的區(qū)別在于在空間上的靈活,數(shù)組需要提前指定長度,不量確定了就不能發(fā)生改變了,比較死板,不夠靈活。比如出現(xiàn)拷貝長度大于了給定的空間,需要再重新定義一個足夠空間的大小,然后把舊空間的內容再一個個拷貝到新的空間,非常麻煩。
c++11引入array主要是用來描述一種支持迭代器的C風格數(shù)組的,所以數(shù)組怎么用,array就怎么用,他定義在棧上,且長度固定不會涉及動態(tài)內存的開辟所以沒有push_back,pop_back的相關方法,但是Vector這種動態(tài)的數(shù)組在開發(fā)更為常用,他底層對象在堆上,且長度是可變的。

vector容器是動態(tài)空間,他隨著元素的加入,它的內部機制會自動擴充空間以容納新的元素。vector的運用對于內存的合理利用與運用的靈活性有很大的幫助。我們再也不必害怕空間不足而一開始就定義一個巨大的數(shù)組了。
實現(xiàn)分析:

空間分配策略
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//動態(tài)擴容的策略(沒內容不先開辟空間,不同于string)
vector<int> v;
for(int i=0;i<100;i++){
cout << "vector容器對象中的有效元素個數(shù):" << v.size() << "vector容器容量的大小:" << v.capacity() << endl;
v.push_back(i);
}
return 0;
}
迭代器非法化問題及解決
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int i=0;i<v.size();i++){
cout << v[i] << " ";
}
cout << endl;
//insert后,迭代器非法化的問題
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
if(0==*it%2){
it=v.insert(it,888);//返回值為迭代器類型
it++;//更新迭代器
}
}
for(int i=0;i<v.size();i++){
cout << v[i] << " ";
}
cout << endl;
//erase迭代器非法化的問題
for(auto it=v.begin();it!=v.end();){
if(0==*it%2){
it=v.erase(it);//返回值為容器類型
}else{
it++;
}
}
for(int i=0;i<v.size();i++){
cout << v[i] << " ";
}
cout << endl;
return 0;
}
泛型算法
泛型算法 = 函數(shù)模板 + 迭代器范圍(注意迭代器的類型) + 函數(shù)符(策略使用)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int i=0;i<v.size();i++){
cout << v[i] << " ";
}
cout << endl;
cout << "----------------使用泛型算法進行遍歷-------------" << endl;
for_each(v.begin(),v.end(),[](int val){cout << val << "-";});
cout << endl;
//針對于容器中有迭代器的容器for_each算法,提供了一種變種:枚舉for循環(huán)
for(int val:v){//val默認為第0個元素,一次遍歷,直到結束為止
cout << val << "-" ;
}
cout << endl;
return 0;
}sort:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
template <class T>
T my_greate(T t1,T t2){
return t1>t2;
}
template <class T>
class my_Greate{
public:
bool get_my_Greate(T t1,T t2){
return t1>t2;
}
};
int main()
{
vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
cout << "---------------------1---------------------" << endl;
//sort,ASC//升序
sort(v.begin(),v.end());
for(int k:v){
cout << k << " ";
}
cout << endl;
cout << "----------------------2--------------------" << endl;
//sort,DESC,使用lambda表達式作為算法策略
sort(v.begin(),v.end(),[](int val1,int val2){return val1>val2;});//一直判斷到最后
//如果val1>val2為真,就把val1排好序,可以把函數(shù)符想象成一個抽象(必須這么理解,因為具體排序要看sort實現(xiàn)了),不是算法的具體實現(xiàn),算法知道意思,就是降序了
for(int k:v){
cout << k << " ";
}
cout << endl;
cout << "----------------------3--------------------" << endl;
//使用函數(shù)對象
sort(v.begin(),v.end(),greater<int>());//這是調用庫函數(shù)中的函數(shù)對象
for(int k:v){
cout << k << " ";
}
cout << endl;
cout << "----------------------4--------------------" << endl;
//定義一個函數(shù)指針,函數(shù)符來實現(xiàn)
sort(v.begin(),v.end(),&my_greate<int>);//這個就是全局函數(shù)指針,函數(shù)符實現(xiàn)
for(int k:v){
cout << k << " ";
}
cout << endl;
//定義一個成員函數(shù)指針,函數(shù)符來實現(xiàn)
//因為成員函數(shù)指針依賴于對象的調用,所以不可以有直接做為策略使用,需要綁定器進行對象綁定才可以
cout << "----------------------5--------------------" << endl;
my_Greate<int> m;
using namespace placeholders;
sort(v.begin(),v.end(),bind(&my_Greate<int>::get_my_Greate,&m,_1,_2));
for(int k:v){
cout << k << " ";
}
cout << endl;
return 0;
}binary_find:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
//sort,ASC
sort(v.begin(),v.end());
for(int k:v){
cout << k << " ";
}
cout << endl;
//時間復雜度O(logn)
bool ok=binary_search(v.begin(),v.end(),70);
if(ok)
cout << "find succeed!" << endl;
else
cout << "find failure!" << endl;
return 0;
}find&&find_if&&bind1st(綁定第一個參數(shù))&&bind2nd(綁定第二個參數(shù))&&bind(新式綁定器):
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main()
{
vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
//find對容器中的值沒有要求,時間復雜度O(n),順序遍歷一遍,和二分查找不一樣,時間復雜度不同
auto it=find(v.begin(),v.end(),70);
if(it!=v.end()){
cout << "find succeed!index:" << it-v.begin() << endl;
}else{
cout << "find failure!" << endl;
}
//find_if按條件查找,需要把一個2這個值按序插入到序列中
it=find_if(v.begin(),v.end(),[](int val){return val < 2;});
if(it!=v.end()){
it=v.insert(it,2);
it++;
cout << "find succeed!" << endl;
}else{
cout << "find failure!" << endl;
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
//老式綁定器bind1st,bind2nd
it=find_if(v.begin(),v.end(),bind1st(greater<int>(),2));
if(it!=v.end()){
it=v.insert(it,2);
it++;
cout << "find succeed!" << endl;
}else{
cout << "find failure!" << endl;
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
//新式綁定器:bind
using namespace placeholders;
it=find_if(v.begin(),v.end(),bind(greater<int>(),2,_1));
if(it!=v.end()){
it=v.insert(it,2);
it++;
cout << "find succeed!" << endl;
}else{
cout << "find failure!" << endl;
}
for(int val:v){
cout << val << " " ;
}
cout << endl;
return 0;
}迭代器與空間配置器

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//實現(xiàn)一個自定的空間配置器
template <class T>
struct Allocate{
//1.開辟空間
T* allocate(size_t size){
T* temp=(T*)malloc(sizeof (T)*size);
if(nullptr==temp){
throw bad_alloc();
}
return temp;
}
//2.構造對象
void constructor(T* p,const T& obj){
//定位new
new (p) T(obj);
}
//3.析構對象
void destructor(T* p){
p->~T();
}
//4.釋放空間
void destroy(T* p){
free(p);
}
};
template <class T,class Allocat=Allocate<T>>
class Vector{
private:
T* _frist;
T* _last;
T* _end;
Allocat _allcater;
public:
Vector(int size=10){
// this->_frist=new T[size];
this->_frist=_allcater.allocate(size);
this->_last=_frist;
this->_end=this->_frist+size;
}
~Vector(){
if(nullptr!=this->_frist){
// delete [] this->_frist;
for(T* p=_frist;p!=_last;p++){
_allcater.destructor(p);
}
}
_allcater.destroy(_frist);
this->_end=this->_last=this->_frist=nullptr;
}
//拷貝構造
Vector(const Vector& other){
int size=other._end-other._frist;
// this->_frist=new T[size];
this->_frist=_allcater.allocate(size);
int len=other._end-other._frist;
memmove(this->_frist,other._frist,sizeof (T)*len);
this->_last=this->_frist+len;
this->_end=this->_frist+size;
}
//等號運算符重載
Vector& operator=(const Vector& other){
if(this==&other){
return *this;
}
int size=other._end-other._frist;
int len=other._last-other._frist;
if(nullptr!=this->_frist){
// delete [] this->_frist;
// this->_frist=new T[size];
for(T* p=_frist;p!=_last;p++){
_allcater.destructor(p);
}
_allcater.destroy(_frist);
this->_frist=_allcater.allcate(size);
}else{
// this->_frist=new T[size];
this->_frist=_allcater.allcate(size);
}
memmove(this->_frist,other._frist,sizeof (T)*len);
this->_last=this->_frist+ len;
this->_end=this->_frist+size;
return *this;
}
bool full(){
return this->_last==this->_end;
}
bool empty(){
return this->_last==this->_frist;
}
//2倍擴容
void expand(){
int size=this->_end-this->_frist;
// T* temp=new T[size* 2];
T* temp=_allcater.allocate(size*2);
memmove(temp,this->_frist,sizeof (T)*size);
// delete [] this->_frist;
for(T* p=_frist;p!=_last;p++){
_allcater.destructor(p);
}
_allcater.destroy(_frist);
this->_frist=temp;
this->_last=this->_frist+size;
this->_end=this->_frist+size*2;
}
void push_back(const T& val){
if(this->full()){
this->expand();
}
// *_last++=val;
_allcater.constructor(this->_last,val);
_last++;
}
void pop_back(){
if(this->empty()){
return;
}
// _last--;
_last--;
_allcater.destructor(_last);
}
int size(){
return this->_last-this->_frist;
}
int capacity(){
return this->_end-this->_frist;
}
T& operator[](int index){
if(index<0||index>=this->size()){
throw out_of_range("越界!");
}
return this->_frist[index];
}
class iterator{
public:
//與標準庫中的泛型算法匹配類型
using difference_type=int;
using value_type= T ;
using pointer=T*;
using reference=T&;
using iterator_category=random_access_iterator_tag;
private:
T* ptr;
public:
iterator(T* ptr=nullptr){
this->ptr=ptr;
}
//迭代器功能:++運算,!=運算
iterator& operator++(){
++this->ptr;
return *this;
}
iterator& operator++(int){
this->ptr++;
return *this;
}
iterator& operator--(){
--this->ptr;
return *this;
}
iterator& operator--(int){
this->ptr--;
return *this;
}
// !=運算符重載:
bool operator!=(const iterator& other){
return this->ptr!=other.ptr;
}
bool operator==(const iterator& other){
return this->ptr==other.ptr;
}
T& operator*(){
return *ptr;
}
T* operator->(){
return ptr;
}
iterator& operator+=(int n){
this->ptr+=n;
return *this;
}
iterator& operator-=(int n){
this->ptr-=n;
return *this;
}
iterator operator+(int n){
T* temp=this->ptr+n;
return iterator(temp);
}
iterator operator-(int n){
T* temp=this->ptr-n;
return iterator(temp);
}
int operator-(const iterator& other){
return this->ptr-other.ptr;
}
bool operator>(const iterator& other){
return this->ptr - other.ptr > 0;
}
bool operator<(const iterator& other){
return this->ptr - other.ptr < 0;
}
bool operator>=(const iterator& other){
return !(*this < other);
}
bool operator<=(const iterator& other){
return !(*this > other);
}
T* get(){
return ptr;
}
};
iterator begin(){
return iterator(this->_frist);
}
iterator end(){
return iterator(this->_end);
}
};
class A{
public:
A(){
cout << "A structure" << endl;
}
~A(){
cout << "A destruct" << endl;
}
A(const A&other){
cout << "A copy" << endl;
}
};
int main()
{
Vector<int> v;
for(int i=0;i<20;i++){
v.push_back(rand()%100+1);
}
for(int i=0;i<v.size();i++){
cout << v[i] << " " ;
}
cout << endl;
Vector<A> v1;
Vector<int> v2;
for(int i=0;i<20;i++){
v2.push_back(rand()%100+1);
}
for(int k:v2){
cout << k << " " ;
}
cout << endl;
return 0;
}deque容器


#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
int main()
{
deque<int> dq;
for(int i=0;i<20;i++){
dq.push_front(rand()%100+1);
}
for(auto it=dq.begin();it!=dq.end();it++){
cout << *it << " ";
}
cout << endl;
sort(dq.begin(),dq.end());
for(int k:dq){
cout << k << " ";
}
cout << endl;
for(int i=0;i<20;i++){
cout << dq.back() << " ";
dq.pop_back();
}
cout << endl;
for(int i=0;i<20;i++){
cout << dq.front() << " ";
dq.pop_front();
}
cout << endl;
return 0;
}容器適配置
容器適配器是沒有迭代器的,不能實現(xiàn)泛型算法
使用deque容器實現(xiàn)一個stack
#include <iostream>
#include <deque>
using namespace std;
template <class T,class Container=deque<T>>
class Stack{
private:
Container container;
public:
void push(const T& val){
container.push_back(val);
}
void pop(){
container.pop_back();
}
T& top(){
return container.back();
}
bool empty(){
return container.empty();
}
};
int main()
{
Stack<int> s;
for(int i=0;i<10;i++){
s.push(i);
cout << i << " ";
}
cout << endl;
while (!s.empty()) {
cout << s.top() << " ";
s.pop();
}
cout << endl;
return 0;
}實現(xiàn)一個單端隊列
#include <iostream>
#include <deque>
using namespace std;
template <class T,class Container=deque<T>>
class SimpleQ{
private:
Container container;
public:
void push(const T& val){
container.push_back(val);
}
void pop(){
container.pop_front();
}
T& top(){
return container.front();
}
bool empty(){
return container.empty();
}
};
int main()
{
SimpleQ<int> q;
for(int i=0;i<10;i++){
q.push(i);
cout << i << " ";
}
cout << endl;
while (!q.empty()) {
cout << q.top() << " ";
q.pop();
}
cout << endl;
return 0;
}優(yōu)先隊列
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T,class Container=vector<T>,class Compair=less<T>>
class Priority_queue{
private:
Container container;
Compair compair;
public:
Priority_queue(){
make_heap(container.begin(),container.end(),compair);
}
void push(const T& val){
container.push_back(val);
// sort(container.begin(),container.end(),compair);//快排,但系統(tǒng)中采取的是大頂堆,原因快排,遞歸過多
push_heap(container.begin(),container.end(),compair);
}
void pop(){
pop_heap(container.begin(),container.end(),compair);
container.pop_back();
}
T& top(){
return container.front();
// return container.back();
}
bool empty(){
return container.empty();
}
};
int main()
{
Priority_queue<int> pq;
for(int i=0;i<20;i++){
pq.push(rand()%100+1);
}
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
return 0;
}list容器

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> l;
for(int i=0;i<20;i++){
l.push_back(rand()%100+1);
}
for(int k:l){
cout << k << " ";
}
cout << endl;
l.sort();
for_each(l.begin(),l.end(),[](int val){cout << val << " ";});
cout << endl;
l.sort([](int val1,int val2){return val1>val2;});
for_each(l.begin(),l.end(),[](int val){cout << val << " ";});
cout << endl;
return 0;
}set容器
Set特性是:所有元素都會根據(jù)元素的鍵值自動被排序。(set 與 map都會按照鍵值來自動排序,只不過set的鍵與值是一體的相同的)Set的元素不像map那樣,可以同時擁有實值與鍵值,set的元素即是鍵值又是實值。(你也可以理解為只有一個值,鍵與值相同)Set不允許兩個元素有相同的鍵值。(即然是自動排序,set與map是不允許有相同的鍵的存在。這一點與map是共同的。),而multiset是可以存相同鍵值的元素。(這是set與multiset的唯一區(qū)別。)。
所以set容器的迭代器是一個常雙向迭代器,只支持什么:++,–,==,!=的操作。
我們可以通過set的迭代器改變set元素的值嗎?不行,因為set元素值就是其鍵值,關系到set元素排序規(guī)則,如果任意改變set元素值,會嚴重破壞set組織結構。換句話說,set的迭代器是一個只讀迭代器。
set容器擁有與list某些相同的性質,當對容器中的元素進行插入操作或者刪除操作的時候,操作之前所有迭代器,在操作完成之后依
multiset的底層實現(xiàn)是紅黑樹,紅黑樹是平衡二叉樹的一種。二叉樹的相關知識點可以百度一下。
#include <iostream>
#include <set>
using namespace std;
class Stu{
private:
string name;
int age;
public:
Stu(string name,int age){
this->age=age;
this->name=name;
}
// bool operator<(const Stu& other)const{
// return this->age<other.age;
// }
void showInfo()const{
cout << "姓名:" << name << ",年齡:" << age << endl;
}
int getAge(){
return this->age;
}
};
template <class T>
class Myless{
public:
bool operator()(T t1,T t2)const{
return t1.getAge()<t2.getAge();
}
};
int main()
{
set<int> s;
for(int i=0;i<20;i++){
s.insert(rand()%100+1);
}
for(int k:s){
cout << k << " ";
}
cout << endl;
cout << "*********************自定義類型放入set****************" << endl;
Stu stu1("yaoliang",19);
Stu stu2("minmin",18);
Stu stu3("sun",17);
// set<Stu> s1;//調用庫中的<預算符重載函數(shù)
// s1.insert(stu1);
// s1.insert(stu2);
// s1.insert(stu3);
// for(const Stu& stu:s1){
// stu.showInfo();
// }
set<Stu,Myless<Stu>> s1;//自定義比較策略
s1.insert(stu1);
s1.insert(stu2);
s1.insert(stu3);
for(const Stu& stu:s1){
stu.showInfo();
}
return 0;
}multiset與set
#include <iostream>
#include <set>
using namespace std;
class Stu{
private:
string name;
int age;
int id;
public:
Stu(string name,int age,int id){
this->age=age;
this->name=name;
this->id=id;
}
bool operator<(const Stu& other)const{
return this->id<other.id;
}
void showInfo()const{
cout << "學號:" << id << ",姓名:" << name << ",年齡:" << age << endl;
}
int getAge(){
return this->id;
}
};
int main()
{
Stu stu1("王大錘",19,1);
Stu stu2("李二狗",18,2);
Stu stu3("張三豐",17,3);
Stu stu4("王五子",17,5);
Stu stu5("劉四思",20,3);
// set<Stu> s1;//調用庫中的<預算符重載函數(shù)
multiset<Stu> s1;
s1.insert(stu1);
s1.insert(stu2);
s1.insert(stu3);
s1.insert(stu5);
s1.insert(stu4);
// pair<set<Stu>::iterator,bool> p;
// p=s1.insert(stu4);
// if(p.second){
// cout << "王五子入學成功!" << endl;
// }else{
// cout << " 王五子入學失??!" << "\n";
// }
for(const Stu& stu:s1){
stu.showInfo();
}
return 0;
}map容器
對組pair構建方式:
#include <iostream>
#include <map>
using namespace std;
int main()
{
pair<string,int> p1("zhangsan",1001);
cout << p1.first << "," << p1.second << endl;
pair<string,int> p2={"lisi",1002};
cout << p2.first << "," << p2.second << endl;
pair<string,int> p3;
p3.first="maliu";
p3.second=1003;
cout << p3.first << "," << p3.second << endl;
pair<string,int> p4=make_pair("wangwu",1004);
cout << p4.first << "," << p4.second << endl;
pair<string,int> p5=map<string,int>::value_type("tianqi",1005);
return 0;
}Map容器的特性是:所有元素都會根據(jù)元素的鍵的值自動排序。Map所有的元素都是統(tǒng)一的pair對組,同時擁有鍵值Key實值Value,pair的第一元素被視為鍵值,第二個元素被視為實值,map不允許兩個元素有相同的鍵。
我們可以通過map迭代器改變map的鍵值嗎?答案是不行,因為map的鍵值關系到map的元素的排列布局,Map中的鍵是不可修改的,但是鍵對應的值是可以修改的。
所以Map的迭代器是一個雙向迭代器,只支持++ == !=操作。
Map是可以隨時插入或刪除鍵值對的。
Map與multimap的唯一區(qū)別是multimap中的鍵是可以重復的。
Map的底層實現(xiàn)機制是由二叉樹中的紅黑樹進行實現(xiàn)的。
#include <iostream>
#include <map>
using namespace std;
int main()
{
pair<string,int> p1("zhangsan",1001);
pair<string,int> p2={"lisi",1002};
pair<string,int> p3;
p3.first="maliu";
p3.second=1003;
pair<string,int> p4=make_pair("wangwu",1004);
pair<string,int> p5=map<string,int>::value_type("tianqi",1005);
map<string,int> m1;
m1.insert(p1);
m1.insert(p2);
m1.insert(p3);
m1.insert(p4);
m1.insert(p5);
for(pair<string,int> p:m1){
cout << p.first << p.second << endl;
}
cout <<m1.at("zhangsan") << endl;
// cout <<m1.at("********") << endl;
cout << m1["zhangsan"] << endl;
cout << "------------------map[] not exist throw-------------" << endl;
cout << m1["**********"] << endl;//副作用,會保存不存在的值
for(pair<string,int> p:m1){
cout << p.first << p.second << endl;
}
cout << "-----------------find-------------" << endl;
auto it=m1.find("lisi");
if(it!=m1.end()){
cout << it->first << "," << it->second << endl;
}else{
cout << "no find!!!" << endl;
}
return 0;
}注意:multimap中是沒有【】中括號運算符的:
map中的[]注意事項:
map中的[]運算符具有兩個作用:
? 1.就是可以在map容器直接插入一個鍵值對。
? 2.當map容器有同名的key時,使用[]也可修飾key對應的value的值。
? 3.注意:在multimap中是沒有[]中括號運算符的。
? 4.中括號運算符如果map沒有這個鍵時,將會自動插入這個鍵值對。
觀察者設計模型
引言
用來解決兩個不相關對象之間的一對一或者一對多的通信模型。
什么是觀察者設計模式
觀察者模式是一種對象行為模式。它定義對象間的一種一對多的依賴關系, 當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。在觀察者模式中,主體是通知的發(fā)布者,它發(fā)出通知時并不需要知道誰是它的觀察者,可以有任意數(shù)目的觀察者訂閱并接受通知。觀察者模式不僅被廣泛應用于軟件界面元素之間的交互,在業(yè)務對象之間的交互、權限管理等方面也有廣泛的應用。
解決的問題
定義了對象間的一種一對多的組合關系,以便一個對象的狀態(tài)發(fā)生時,所有依賴于它的對象都得到通知并自動刷新。
觀察者和被觀察者之間存在“觀察”的邏輯關系,當被觀察者發(fā)生變化時,觀察者就會觀察到這樣的變化,并作出相應的響應。
編程思路
設定兩者類,一個為觀察者類,一個為被觀察者類
觀察者類中,定義一個對某個事件感興趣的處理函數(shù),一般也叫做槽函數(shù)
被觀察者類中,定義一個數(shù)據(jù)結構,用來保存觀察者對某一個事件id(信號)感興趣,使用數(shù)據(jù)結構建立信號與對象之間的映射關系
被觀察者類中,定義兩個方法函數(shù):
一個方法為:添加觀察者與其感興趣的事件id(信號)加入到容器中
另一個方法為:信號函數(shù):通知事件函數(shù)執(zhí)行邏輯:首先遍歷容器中,有沒有感興趣的事件ID,如果有,則代表一系列的觀察者,對這個事件感興趣,那么再次遍歷觀察者列表,讓每一個觀察者執(zhí)行相應的槽函數(shù)
#include <iostream>
#include <map>
#include <list>
using namespace std;
class RecvBase
{
public:
RecvBase(){
cout << "--------------RecvBase structure------------------" << endl;
}
virtual void slotFunctions(int msgid)=0;
virtual~RecvBase()
{
cout << "--------------RecvBase destruct------------" << endl;
}
};
class Recv:public RecvBase
{
public:
void slotFunctions(int msgid)override
{
switch(msgid)
{
case 1:
cout << "接收到1信號,執(zhí)行信號1對應槽函數(shù)邏輯" << endl;
break;
case 2:
cout << "接收到2信號,執(zhí)行信號2對應槽函數(shù)邏輯" << endl;
break;
case 3:
cout << "接收到3信號,執(zhí)行信號3對應槽函數(shù)邏輯" << endl;
break;
case 4:
cout << "接收到4信號,執(zhí)行信號4對應槽函數(shù)邏輯" << endl;
break;
}
}
Recv()
{
cout << "--------------structure--------------" << endl;
}
~Recv()override
{
cout << "--------------destruct------------" << endl;
}
};
class Sender
{
public:
map<int,list<RecvBase*>> recvMap;
void addRecvToMap(int msgid,RecvBase* recv)
{
this->recvMap[msgid].push_back(recv);
}
void signals(int msgid)
{
auto it=recvMap.find(msgid);
if(it!=recvMap.end())
{
for(RecvBase* p:it->second)
p->slotFunctions(msgid);
}else
{
cout << "接受到未知信號,沒有信號對應的槽函數(shù)邏輯" << endl;
}
}
};
int main(){
Sender sender;
RecvBase* r1=new Recv();
RecvBase* r2=new Recv();
RecvBase* r3=new Recv();
RecvBase* r4=new Recv();
sender.addRecvToMap(1,r1);
sender.addRecvToMap(2,r2);
sender.addRecvToMap(3,r3);
sender.addRecvToMap(4,r4);
int msgid;
while(true)
{
cin >> msgid;
if(-1==msgid)break;
sender.signals(msgid);
}
delete r1;
delete r2;
delete r3;
delete r4;
return 0;
}到此這篇關于C++標準模板庫STL深入講解的文章就介紹到這了,更多相關C++標準模板庫內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Qt實現(xiàn)編輯數(shù)據(jù)庫數(shù)據(jù)的方法詳解
這篇文章主要為大家詳細介紹了Qt是如何實現(xiàn)編輯數(shù)據(jù)庫數(shù)據(jù)的,文中的示例代碼簡潔易懂,對我們深入了解QT有一定的幫助,感興趣的小伙伴可以了解一下2023-02-02

