STL(標準模板庫),是目前C++內置支持的library。它的底層利用了C++類模板和函數模板的機制,由三大部分組成:容器、算法和迭代器。前端
vector就是動態數組。在堆中分配內存,元素連續存放,有保留內存,若是減小大小後,內存也不會釋放。ios
若是新值>當前大小時纔會再分配內存。c++
它擁有一段連續的內存空間,而且起始地址不變,所以它能很是好的支持隨即存取,即[]操做符,但因爲它的內存空間是連續的,因此在中間進行插入和刪除會形成內存塊的拷貝。算法
當該數組後的內存空間不夠時,須要從新申請一塊足夠大的內存並進行內存的拷貝。這些都大大影響了vector的效率。底層數據結構爲數組 ,支持快速隨機訪問。swift
【vector總結】數組
須要常常隨機訪問請用vector緩存
【vector的基本用法】bash
front()返回頭部元素的引用,能夠當左值數據結構
back()返回尾部元素的引用,能夠當左值less
push_back()添加元素,只能尾部添加
pop_back()移除元素,只能在尾部移除
int main(int argc, const char * argv[]) {
//定義一個vector容器
vector<int> v1;
//插入元素(尾部插入)
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
//迭代器遍歷打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//修改頭部元素的值(front()返回是引用,能夠當左值)
v1.front() = 44;
//輸出頭部元素
cout<< "頭部元素:" << v1.front() << endl;
//修改尾部的值(back()返回是引用,能夠當左值)
v1.back() = 99;
//輸出尾部元素
cout << "尾部元素" << v1.back() <<endl;
//刪除元素(從尾部刪除)
v1.pop_back();
//迭代器遍歷打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
【vector的初始化】
vector有4種方式初始化,有直接初始化,也有經過拷貝構造函數初始化。
int main(int argc, const char * argv[]) {
//直接構造函數初始化
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
//經過拷貝構造函數初始化
vector<int> v2 = v1;
//使用部分元素來構造
vector<int> v3(v1.begin(), v1.begin() + 1);
vector<int> v4(v1.begin(), v1.end());
//存放三個元素,每一個元素都是9
vector<int> v5(3,9);
return 0;
}
複製代碼
【vector的遍歷】
vector的遍歷有多種方式,能夠根據[]或者迭代器遍歷。
[]方式,若是越界或出現其餘錯誤,不會拋出異常,可能會崩潰,可能數據隨機出現
at方式,若是越界或出現其餘錯誤,會拋出異常,須要捕獲異常並處理
迭代器提供了逆向遍歷,能夠經過迭代器來實現逆向遍歷,固然上面兩種方式也能夠
int main(int argc, const char * argv[]) {
//建立vector
vector<int> v1;
//插入元素
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
//遍歷-[]取值
for (int i = 0; i < v1.size(); i++) {
cout << v1[i] << " ";
}
cout << endl;
//遍歷-at取值
for (int i = 0; i < v1.size(); i++) {
cout << v1.at(i) << " ";
}
cout << endl;
//遍歷-迭代器遍歷
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//遍歷-迭代器逆向遍歷
for (vector<int>::reverse_iterator it = v1.rbegin(); it != v1.rend(); it++) {
cout << *it << " ";
}
cout << endl;
//測試越界
cout << "[]越界:" << v1[20] << endl; //不會拋出異常,可能會崩潰,可能會亂碼
cout << "at越界:" << v1.at(20) << endl; //會拋出異常,須要捕獲異常
return 0;
}
複製代碼
【vector的push_back強化】
push_back是在當前vector的內存末尾拷貝元素進入容器。注意這個地方可能產生淺拷貝,因此容器中的對象要支持拷貝操做。另外,若是vector初始化了個數,而不初始化具體的值,push_back也只會在最後面追加。
int main(int argc, const char * argv[]) {
//初始化10個元素的容器
vector<int> v(10);
//打印容器大小
cout << v.size() << endl;
//push_back添加元素
v.push_back(100);
//打印容器大小
cout << v.size() << endl;
//遍歷後的結果是 0 0 0 0 0 0 0 0 0 0 100
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
【vector的元素刪除】
vector的刪除,是根據位置進行刪除,若是想要刪除某個元素,須要找到當前元素的迭代器位置,再進行刪除。
erase(iterator)函數,刪除後會返回當前迭代器的下一個位置。
int main(int argc, const char * argv[]) {
//1 建立容器並初始化
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++) {
v1[i] = i;
}
//2 區間刪除
//--2.1 刪除前3個元素
v1.erase(v1.begin(), v1.begin() + 3);
//--2.2 刪除指定位置的元素
v1.erase(v1.begin() +3);
//3 根據元素的值進行刪除,刪除值爲2的元素
v1.push_back(2);
v1.push_back(2);
vector<int>::iterator it = v1.begin();
while (it != v1.end()) {
if (*it == 2) {
it = v1.erase(it); //刪除後,迭代器指針會執行下一個位置並返回。
}else{
it++;
}
}
//4 遍歷打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
【vector的插入元素】
vector提供了insert函數,結合迭代器位置插入指定的元素。若是迭代器位置越界,會拋出異常。
int main(int argc, const char * argv[]) {
//初始化vector對象
vector<int> v1(10);
//在指定的位置插入元素10的拷貝
v1.insert(v1.begin() + 3, 10);
//在指定的位置插入3個元素11的拷貝
v1.insert(v1.begin(), 3, 11);
//遍歷
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
list就是雙向鏈表,在堆中存放,每一個元素都是放在一塊內存中,它的內存空間能夠是不連續的,經過指針來進行數據的訪問,這個特色使得它的隨機存取變的很是沒有效率,所以它沒有提供[]操做符的重載。
但它能夠以很好的效率支持任意地方的刪除和插入。
list沒有空間預留習慣,因此每分配一個元素都會從內存中分配,每刪除一個元素都會釋放它佔用的內存.底層數據結構爲雙向鏈表,支持快速增刪
【list總結】
若是你喜歡常常添加刪除大對象的話,那麼請使用list 要保存的對象不大,構造與析構操做不復雜,那麼可使用vector代替 list<指針>徹底是性能最低的作法,這種狀況下仍是使用vector<指針>好,由於指針沒有構造與析構,也不佔用很大內存
【list的基本操做】
int main(int argc, const char * argv[]) {
//建立list對象
list<int> l;
//尾部添加元素
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
//頭部添加元素
l.push_front(111);
//遍歷
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
//list不能隨機訪問
list<int>::iterator it = l.begin();
it++;
it++;
cout << *it <<endl;
// it = it + 1; //編譯報錯,不能隨機訪問
// it = it + 5; //編譯報錯,不能隨機訪問
return 0;
}
複製代碼
【list的刪除】
list提供了兩個函數用來刪除元素,分別是erase和remove。
erase是經過位置或者區間來刪除,主要結合迭代器指針來操做
remove是經過值來刪除
int main(int argc, const char * argv[]) {
//建立list對象
list<int> l;
//添加數據
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
l.push_back(100);
l.push_back(100);
//刪除頭部元素
l.erase(l.begin());
//刪除某個區間
list<int>::iterator it = l.begin();
it++;
it++;
it++;
l.erase(l.begin(), it);
//移除值爲100的全部元素
l.remove(100);
//遍歷
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
[堆1] --> [堆2] -->[堆3] -->...
每一個堆保存好幾個元素,而後堆和堆之間有指針指向,看起來像是list和vector的結合品.
它支持[]操做符,也就是支持隨機存取,可讓你在前面快速地添加刪除元素,或是在後面快速地添加刪除元素,而後還能夠有比較高的隨機訪問速度,和vector的效率相差無幾。
它支持在兩端的操做:push_back,push_front,pop_back,pop_front等,而且在兩端操做上與list的效率也差很少。
在標準庫中vector和deque提供幾乎相同的接口,在結構上它們的區別主要在於這兩種容器在組織內存上不同,deque是按頁或塊來分配存儲器的,每頁包含固定數目的元素.相反vector分配一段連續的內存,vector只是在序列的尾段插入元素時纔有效率,而deque的分頁組織方式即便在容器的前端也能夠提供常數時間的insert和erase操做,並且在體積增加方面也比vector更具備效率
【deque操做】
int main(int argc, const char * argv[]) {
//定義deque對象
deque<int> d1;
//尾部插入元素
d1.push_back(10);
d1.push_back(20);
d1.push_back(30);
//頭部插入元素
d1.push_front(1);
d1.push_front(2);
d1.push_front(3);
//尾部刪除元素
d1.pop_back();
//頭部刪除元素
d1.pop_front();
//修改頭部和尾部的值
d1.front() = 111;
d1.back() = 222;
//查找元素爲1的下標
//經過distance求取下標
deque<int>::iterator it = d1.begin();
while (it != d1.end()) {
if (*it == 1) {
cout << "下標:" << distance(d1.begin(), it) << endl;
}
it++;
}
//遍歷
for (deque<int>::iterator it = d1.begin(); it != d1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
複製代碼
【vector、list、deque總結】
vector是能夠快速地在最後添加刪除元素,並能夠快速地訪問任意元素
list是能夠快速地在全部地方添加刪除元素,可是隻能快速地訪問最開始與最後的元素
deque在開始和最後添加元素都同樣快,並提供了隨機訪問方法,像vector同樣使用[]訪問任意元素,可是隨機訪問速度比不上vector快,由於它要內部處理堆跳轉 deque也有保留空間.另外,因爲deque不要求連續空間,因此能夠保存的元素比vector更大,這點也要注意一下.還有就是在前面和後面添加元素時都不須要移動其它塊的元素,因此性能也很高。
所以在實際使用時,如何選擇這三個容器中哪個,應根據你的須要而定,通常應遵循下面的原則:
一、若是你須要高效的隨即存取,而不在意插入和刪除的效率,使用vector
二、若是你須要大量的插入和刪除,而不關心隨即存取,則應使用list
三、若是你須要隨即存取,並且關心兩端數據的插入和刪除,則應使用deque。
底層通常用list或deque實現,封閉頭部便可,不用vector的緣由應該是容量大小有限制,擴容耗時
▽ 基礎數據類型的stack
int main(int argc, const char * argv[]) {
//定義stack對象
stack<int> s1;
//入棧
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
//打印棧頂元素,並出棧
while (!s1.empty()) {
//取出棧頂元素
cout << "當前棧頂元素" << s1.top() << endl;
//獲取棧的大小
cout << "當前棧的大小" << s1.size() << endl;
//出棧
s1.pop();
}
return 0;
}
複製代碼
▽ 複雜數據類型的stack
//定義類
class Teacher {
public:
char name[32];
int age;
void printT() {
cout << "age = " << age << endl;
}
};
int main(int argc, const char * argv[]) {
Teacher t1, t2, t3;
t1.age = 22;
t2.age = 33;
t3.age = 44;
//定義棧容器
stack<Teacher> s1;
//入棧
s1.push(t1);
s1.push(t2);
s1.push(t3);
//出棧並打印
while (!s1.empty()) {
//打印棧頂元素
Teacher tmp = s1.top();
tmp.printT();
//出棧
s1.pop();
}
return 0;
}
複製代碼
底層通常用list或deque實現,封閉頭部便可,不用vector的緣由應該是容量大小有限制,擴容耗時 (stack和queue實際上是適配器,而不叫容器,由於是對容器的再封裝)
#include <queue>
void main() {
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << "對頭元素" << q.front() <<endl;
cout << "隊列的大小 " << q.size() <<endl;
while (!q.empty()) {
int tmp = q.front();
cout << tmp << " ";
q.pop();
}
}
class Teacher {
public:
int age;
char name[32];
void printT() {
cout << "age :" << age <<endl;
}
}
void main() {
Teacher t1,t2,t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
queue<Teacher > q;
q.push(t1);
q.push(t2);
q.push(t3);
while (!q.empty()) {
Teacher tmp = q.front();
tmp.printT();
q.pop();
}
}
複製代碼
底層數據結構通常爲vector爲底層容器,堆heap爲處理規則來管理底層容器實現
優先級隊列分爲:最小值優先隊列和最大值優先隊列。
此處的最大值、最小值是指隊頭的元素(增序、降序)。默認,是建立最大值優先級隊列。 定義優先級的方法:
priority_queue默認定義int類型的最大值隊列
priority_queue<int, vector, less>定義int型的最大值優先隊列
priority_queue<int, vector, greater>定義int型的最小值隊列
上面的定義中,less和greater至關於謂詞,是預約義好的排序函數,稱之爲「仿函數」。
void main() {
//定義優先級隊列(默認是最大值優先級隊列)
priority_queue<int> p1;
//定義一個最大優先級隊列
//less是提早定義好的預約義函數 至關於謂詞
priority_queue<int, vector<int>, less<int>> p2;
//定義一個最小值優先級隊列v
priority_queue<int, vector<int>, greater<int>> p3;
//給默認的最大優先級隊列入棧
p1.push(33);
p1.push(11);
p1.push(55);
p1.push(22);
//打印最大優先級的對頭元素
cout<<"對頭元素:"<< p1.top() <<endl;
cout<<"隊列的大小:"<< p1.size() <<endl;
//給最小值優先級隊列入棧
p3.push(33);
p3.push(11);
p3.push(55);
p3.push(22);
//打印最小優先級隊列的對頭元素
cout<<"對頭元素:"<< p3.top() <<endl;
cout<<"隊列的大小:"<< p3.size() <<endl;
}
複製代碼
集合, 用來判斷某一個元素是否是在一個組裏面,使用的比較少,底層數據結構爲紅黑樹,有序,不重複
【set元素的插入和刪除】
set提供了insert和erase函數,用來對元素進行插入和刪除操做。
基礎類型數據,若是插入的是重複的元素,則插入失敗,返回值是一個pair類型(pair類型相似於swift語言中的元組的概念,經過其判斷是否插入成功)
複雜類型數據,須要經過仿函數來肯定元素的順序,判斷是不是重複元素。
void main() {
set<int> set1;
//插入元素
for (int i = 0; i<5; i++) {
int tmp = rand();
set1.insert(tmp);
}
//重複插入元素(會插入不成功,下一節會分析若是根據返回值判斷是否插入成功)
set1.insert(100);
set1.insert(100);
set1.insert(100);
set1.insert(100);
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << *it <<" ";
}
//刪除集合
while(!set1.empty())
{
//獲取頭部
set<int>::iterator it = set1.begin();
//打印頭部元素
cout << *it << endl;
//從頭部刪除元素
set1.erase(set1.begin());
}
}
複製代碼
【普通數據類型的排序】
set容器是有序的集合,默認的順序是從小到大的。建立集合的方式:
set建立默認的從小到大的int類型的集合
set<int,less>建立一個從小到大的int類型的集合
set<int,greater>建立一個從大到小的int類型的集合
上面的less和greater就是仿函數,集合會根據這個仿函數的返回值是否爲真類進行排序。
//仿函數的原型,下面是C++提供的默認的greater的仿函數(刪除了宏定義後的)
struct greater {
bool operator()(const int &left, const int &right) const {
//若是左值>右值,爲真。從大到小的排列
return left > right;
}
};
複製代碼
添加進set集合的元素確實是有序的。
void main() {
//默認,從小到大
set<int> set1;
//從小到大--默認就是
set<int, less<int>> set2;
//從大到小
set<int, greater<int>> set3;
//添加元素
for (int i = 0; i < 5; i++) {
int tmp = rand();
set3.insert(tmp);
}
//遍歷
for (set<int>::iterator it = set3.begin(); it != set3.end(); it++) {
cout<< *it << " ";
}
}
複製代碼
【自定義對象的排序】
基礎數據類型的set是有序的關鍵緣由是greater和less仿函數。
自定義對象的有序是經過自定義仿函數來實現。
仿函數,之因此叫仿函數,是由於它跟函數很像,但並非一個函數。它的結果以下,只要咱們實現了這個仿函數,咱們也能夠對自定義對象進行排序。
//定義仿函數的結構體
struct FunctionName {
//重載了()運算符,實現兩個自定義對象的比較
bool opeartor() (Type &left, Type &right) {
//左值大於右值,從大到小的順序
if(left > right)
return true;
else
return false;
}
};
複製代碼
下面,咱們自定義一個Student對象,根據年齡進行排序,將對象加入到set集合中,並進行打印。
//定義student對象
class Student {
public:
Student(const char *name, int age)
{
strcpy(this->name, name);
this->age = age;
}
public:
char name[64];
int age;
};
//提供仿函數,用於自定義對象的set進行排序,要寫一個仿函數,用來排序
struct FuncStudent {
//重載了括號操做符,用來比較大小
bool operator() (const Student &left, const Student &right) {
//若是左邊比右邊小,從小到大按照年齡排序
if(left.age < right.age)
return true;
else
return false;
}
};
void main() {
Student s1("s1",32);
Student s2("s2",22);
Student s3("s3",44);
Student s4("s4",11);
Student s5("s5",22);
//建立集合,採用從小到大的排序
set<Student, FuncStudent> set1;
//插入數據
set1.insert(s1);
set1.insert(s2);
set1.insert(s3);
set1.insert(s4);
//插入不進去(年齡有重複的,因此插不進去了),要經過返回值來確保是否插入成功
set1.insert(s5);
//遍歷
for (set<Student>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << it->age << "\t" << it->name <<endl;
}
}
複製代碼
【pair類型的返回值】
pair類型,就相似於Swift語言中的「元組」的概念,這個類型包含了多個數據類型,在函數返回的時候,能夠同時返回多個值。
它其實是一個結構體。它包含了兩個屬性,first和second。
template <class _T1, class _T2> struct pair {
typedef _T1 first_type;
typedef _T2 second_type;
_T1 first;
_T2 second;
}
複製代碼
set集合中的元素是惟一的,重複的元素插入會失敗。
判斷是否插入成功,咱們能夠經過insert函數的返回值來判斷,它的返回值是一個pair類型。
insert函數的原型: pair<iterator,bool> insert(const value_type& __v)
返回的是pair<iterator, bool>類型,pair的第一個屬性表示當前插入的迭代器的位置,第二個屬性表示插入是否成功的bool值。咱們能夠經過第二個屬性來判斷元素是否插入成功。
//pair的使用判斷set的insert函數的返回值
void test3() {
Student s1("s1",32);
Student s2("s2",22);
Student s3("s3",44);
Student s4("s4",11);
Student s5("s5",22);
//建立集合,採用從小到大的排序
set<Student, FuncStudent> set1;
//插入數據,接收返回值
pair<set<Student, FuncStudent>::iterator, bool> pair1 = set1.insert(s1);
if (pair1.second == true) {
cout << "插入s1成功" <<endl;
}else{
cout << "插入s1失敗" <<endl;
}
複製代碼
【set查找元素】
set容器提供了多個函數用來查找元素
iterator find(const key_type& __k) find函數查找元素爲k的迭代器位置
iterator lower_bound(const key_type& __k) lower_bound函數查找小於等於元素k的迭代器位置
iterator upper_bound(const key_type& __k) upper_bound函數查找大於元素k的迭代器位置
pair<iterator,iterator> equal_range(const key_type& __k) equal_range函數返回一個pair類型,第一個屬性表示大於等於k的迭代器位置,第二個是大於k的迭代器位置
void test4() {
set<int> set1;
for (int i = 0; i < 10; i++) {
set1.insert(i+1);
}
//遍歷
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << *it <<endl;
}
//查找元素是5的迭代器位置
set<int>::iterator it0 = set1.find(5);
cout << "it0:" << *it0 <<endl;
//查找小於等於5的元素迭代器位置
set<int>::iterator it1 = set1.lower_bound(5);
cout << "it1:" << *it1 <<endl;
//查找大於5的元素迭代器位置
set<int>::iterator it2 = set1.upper_bound(5);
cout << "it2:" << *it2 <<endl;
//返回的pair第一個迭代器是>=5,另外一個是>5
pair<set<int>::iterator, set<int>::iterator> mypair = set1.equal_range(5);
set<int>::iterator it3 = mypair.first;
set<int>::iterator it4 = mypair.second;
cout << "it3:" << *it3 <<endl;
cout << "it4:" << *it4 <<endl;
}
複製代碼
底層數據結構爲紅黑樹,有序,可重複
multiset容器,與set容器類似,可是multiset容器中的元素能夠重複。另外,他也是自動排序的,容器內部的值不能隨便修改,由於有順序的。
void test5() {
//定義multiset
multiset<int> set1;
//從鍵盤不停的接收值
int tmp = 0;
printf("請輸入multiset集合的值:");
scanf("%d", &tmp);
while (tmp != 0) {
set1.insert(tmp);
scanf("%d", &tmp);
}
//迭代器遍歷
for (multiset<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout<< *it <<" ";
}
cout <<endl;
//刪除
while (!set1.empty()) {
multiset<int>::iterator it = set1.begin();
cout << *it << " ";
set1.erase(it);
}
}
複製代碼
映射,至關於字典,把一個值映射成另外一個值,若是想建立字典的話使用它。底層採用的是樹型結構,多數使用平衡二叉樹實現,查找某一值是常數時間,遍歷起來效果也不錯,只是每次插入值的時候,會從新構成底層的平衡二叉樹,效率有必定影響。底層數據結構爲紅黑樹,有序,不重複
【map元素的插入與刪除】
int main() {
map<int, string> map1;
//insert方法插入
//--1 經過pair<int, string>(1,」chenhua「) 構造pair元素
map1.insert(pair<int, string>(1,"chenhua"));
//--2 經過make_pair構造pair元素
map1.insert(make_pair(2,"mengna"));
//--3 經過value_type構造pair元素
map1.insert(map<int, string>::value_type(3,"chenmeng"));
//[]直接插入
map1[4] = "menghua";
//重複插入(插入會不成功)
pair<map<int, string>::iterator, bool> pair1 = map1.insert(make_pair(2, "haha"));
if (pair1.second) {
cout << "重複插入成功" << endl;
}else{
cout << "重複插入失敗" << endl;
}
//元素的修改
//map[1] = "22"的方式,若是不存在鍵則插入,存在鍵則修改
map1[2] = "haha";
//元素的刪除
//--刪除值爲"haha"的元素
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
if (it->second.compare("haha") == 0) {
map1.erase(it);
}
}
//遍歷
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
cout << it->first << "\t" << it->second << endl;
}
return 0;
}
複製代碼
【map元素的查找】
map提供了兩個函數進行key的查找:find和equal_range。
int main() {
//定義map
map<int ,string> map1;
map1[1] = "chenhua";
map1[2] = "mengna";
//查找key=100的鍵值對
map<int, string>::iterator it = map1.find(100);
if (it != map1.end()) {
cout << "存在key=100的鍵值對";
}else{
cout << "不存在" << endl;
}
//查找key = 1000的位置
//返回兩個迭代器,第一個表示<=1000的迭代器位置,第二個是>1000的迭代器位置
pair<map<int, string>::iterator, map<int, string>::iterator> mypair = map1.equal_range(1000);
if (mypair.first == map1.end()) {
cout << "大於等於5的位置不存在" << endl;
}else{
cout << mypair.first->first << "\t" << mypair.first->second << endl;
}
if (mypair.second == map1.end()) {
cout << "大於5的位置不存在" << endl;
}else{
cout << mypair.second->first << "\t" << mypair.second->second << endl;
}
return 0;
}
複製代碼
底層數據結構爲紅黑樹,有序,可重複
multimap容器,與map容器的惟一區別是:multimap支持多個鍵值。
因爲支持多個鍵值,multimap提供了cout函數來計算同一個key的元素個數。
class Person {
public:
string name; //姓名
int age; //年齡
string tel; //電話
double sal; //工資
};
void test() {
Person p1,p2,p3,p4,p5;
p1.name = "王1";
p1.age = 31;
p2.name = "王2";
p2.age = 31;
p3.name = "張3";
p3.age = 31;
p4.name = "張4";
p4.age = 31;
p5.name = "錢5";
p5.age = 31;
multimap<string, Person> map2;
//sale部門
map2.insert(make_pair("sale", p1));
map2.insert(make_pair("sale", p2));
//development部門
map2.insert(make_pair("development", p3));
map2.insert(make_pair("development", p4));
//Finanncial部門
map2.insert(make_pair("Finanncial", p5));
//遍歷
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {
cout << it->first << "\t" << it->second.name << endl;
}
//按部門顯示員工信息
int developNum = (int) map2.count("development");
cout << "development部門人數:" << developNum << endl;
multimap<string,Person>::iterator it2 = map2.find("development");
int tag = 0;
while (it2 != map2.end() && tag < developNum) {
cout << it2->first << "\t" << it2->second.name <<endl;
it2 ++;
tag ++;
}
//把age=32 修改name= 32
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {
if (it->second.age == 32) {
it->second.name = "32";
}
}
}
int main(int argc, const char * argv[]) {
test();
return 0;
}
複製代碼
底層數據結構爲hash表,無序,不重複
底層數據結構爲hash表,無序,可重複
底層數據結構爲hash表,無序,不重複
底層數據結構爲hash表,無序,可重複
STL的容器主要利用了C++的模板特性來實現。須要注意:
針對容器,容器之間也支持拷貝。因此須要注意:
【STL容器的元素拷貝】 若是容器元素若是沒有實現拷貝構造函數,出現淺拷貝後的崩潰問題。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Student {
public:
Student(const char *name, int age)
{
cout << "構造函數" << endl;
//分配內存空間
m_name = new char[strlen(name) + 1];
//值拷貝
strcpy(m_name, name);
m_age = age;
}
~Student()
{
printf("%p 指向的空間 調用析構函數\n", m_name);
if (m_name != NULL) {
delete []m_name;
m_age = 0;
}
}
private:
char *m_name;
int m_age;
};
int main() {
Student s1("chenhua",24);
vector<Student> v1;
v1.push_back(s1);
return 0;
}
複製代碼
上面的代碼段,運行後的結果以下:
構造函數
0x100302a00 指向的空間 調用析構函數
0x100302a00 指向的空間 調用析構函數
複製代碼
運行後,打印出結果後並報錯。報錯緣由是同一個內存空間被釋放了2次,致使的崩潰。
其根本緣由是,v1將s1拷貝到容器,因爲Student沒有重寫拷貝構造函數,從而出現了淺拷貝,只拷貝了地址。釋放的時候毫無疑問出現錯誤。 若是咱們給Student重寫了拷貝構造函數和重載了等號操做符,則上面的錯誤就不會出現。
//重寫拷貝構造函數
Student(const Student &obj)
{
//分配內存空間
m_name = new char[strlen(obj.m_name) + 1];
//值拷貝
strcpy(m_name, obj.m_name);
m_age = obj.m_age;
}
//重載等號運算符
Student & operator=(const Student &obj)
{
//釋放舊值
if (m_name != NULL) {
delete [] m_name;
m_age = 0;
}
//分配內存空間並賦值
m_name = new char[strlen(obj.m_name) + 1];
strcpy(m_name, obj.m_name);
m_age = obj.m_age;
return *this;
}
複製代碼