一、STL概述
STL主要分为分为三类:
algorithm(算法) - 对数据进行处理(解决问题) 步骤的有限集合
container(容器) - 用来管理一组数据元素
Iterator (迭代器) - 可遍历STL容器内全部或部分元素”的对象
容器和算法通过迭代器可以进行无缝地连接。在STL中几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
STL 最早源于惠普实验室,早于C++存在,但是C++引入STL概念后,STL就成为C++的一部分,因为它被内建在你的编译器之内,不需要另行安装。
STL被组织为下面的13个头文 件:< algorithm>、< deque>、< functional>、< iterator>、< vector>、< list>、< map>、< memory>、< numeric>、< queue>、< set>、< stack> 和< utility>。
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
class student{
public:
student(int age, const char *name){
this->age = age;
strncpy(this->name, name, 64);
}
student(const student &s){
this->age = s.age;
strncpy(this->name, s.name, 64);
cout<<"拷贝构造函数被调用!"<<endl;
}
public:
int age;
char name[64];
};
void demo1(){
//第一部分 容器
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(3);
cout<<"v1 的元素个数:"<<v1.size()<<endl;
cout<<"v1中保存的元素:"<<endl;
//方式1,下标访问
for(unsigned int i=0; i<v1.size(); i++){
cout<<v1[i]<<endl;
}
//方式2,迭代器访问
//第二部分 迭代器
//it
vector<int>::iterator it = v1.begin();
for( ; it != v1.end(); it++){
cout<< *it <<endl;
}
//第三部分 算法
int ncount = count(v1.begin(), v1.end(), 90);
cout<<"v1 中数值为 90 的元素个数:"<< ncount<< endl;
}
void demo2(){
//容器中直接存放对象,会发生拷贝构造
vector<student> v1;
student s1(18, "李小美");
student s2(19, "王大帅");
//动态数组增加元素
v1.push_back(s1);
v1.push_back(s2);
cout<<"v1 的学生的个数:"<<v1.size()<<endl;
//方式1,下标访问
//for(unsigned int i=0; i<v1.size(); i++){
// cout<<v1[i].name<<": "<<v1[i].age<<endl;
//}
vector<student>::iterator it = v1.begin();
for( ; it != v1.end(); it++){
cout<< (*it).name<<": "<<(*it).age <<endl;
}
}
//容器中存放指针
void demo3(){
vector<student *> v1;
student s1(18, "李小美");
student s2(19, "王大帅");
v1.push_back(&s1);
v1.push_back(&s2);
cout<<"v1 的学生的个数:"<<v1.size()<<endl;
//方式1,下标访问
//for(unsigned int i=0; i<v1.size(); i++){
// cout<<v1[i].name<<": "<<v1[i].age<<endl;
//}
vector<student *>::iterator it = v1.begin();
for( ; it != v1.end(); it++){
cout<< (**it).name<<": "<<(**it).age <<endl;
}
}
void main(){
demo3();
system("pause");
return ;
}
总结:
上面例子的知识点:
容器:
- 容器可以放任意类型的数据,对于普通类型就是直接存放;对于类类型会发生拷贝构造函数,进行值传递(即vector会新开辟空间来存放拷贝过来的值,而不是直接存放变量),此时访问容器,需要用到一级指针。
- 如果容器存放的是类类型的指针,那么就不会在存放时调用拷贝构造了,因为存放的是对象的地址。此时访问容器,需要用到二级指针。
所以对于存放类类型,尽量都存放指针,避免过多的调用拷贝构造,消耗空间。
迭代器:
迭代器是一个指针,指向容器中的元素。
begin是指向容器中的首元素,end是指向容器中最后一个元素的后一个位置。
在使用迭代器的过程中,不要随意的插入删除元素,否则会导致迭代器失效。
算法:
这里用count算法来计算容器中的元素数量或指定值的元素数量。
二、容器
在实际的开发过程中,数据结构本身的重要性完全不逊于算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要。
经典的数据结构数量有限,但是在项目实战中,我们常常重复着一些为了存放不同数据类型而实现顺序表、链表等结构而重复编写的代码,这些代码都十分相似,只是为了适应不同数据类型的变化而在细节上有所出入。STL容器就为我们提供了这样的方便,它允许我们重复利用已有的实现构造自己的特定类型下的数据结构,通过设置一些模板,STL容器对最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,避免重复编码。
容器部分主要有由< vector>,< list>,< deque>,< set>,< map>,< stack> 和< queue>组成。
下面是常用的一些容器,可以通过下表总结一下它们和相应头文件的对应关系。
1.vector容器
①vector容器概念
vector是将元素置于一个动态数组中加以管理的容器。即会自动扩容。
vector可以随机存取元素,支持索引值直接存取, 用[]操作符或at()方法对元素进行操作
vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时。因为为了保证连续性,中部或头部插入移除时,其他元素也会相应地挪动位置。
②vector对象的构造
vector采用模板类实现,vector对象的默认构造形式:vector< T> vecT;
默认构造函数:
vector< int> v1; //一个存放int的vector容器
vector< float> v2; //一个存放float的vector容器
vector< student> v2; //一个存放student的vector容器
带参构造函数:
vector(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间
vector(n,elem); //构造函数将n个elem拷贝给本身
vector(const vector &v1); //拷贝构造函数
// demo 15-23
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
void demo1(){
//vector 对象的默认构造
//默认构造函数 元素个数为0, 所占内存空间为0
vector<int> v1;
//vector<float> v2;
cout<<"v1 的元素个数: "<<v1.size()<<endl;
cout<<"v1 容器的大小:"<<v1.capacity()<<endl;
//当我们使用vector的默认构造函数时,切记,在还没插入元素前,不能直接通过下标去访问,因为容器还没分配空间
//v1[0]=1;
v1.push_back(1);
cout<<"尾部插入1个元素后:"<<endl;
cout<<"v1 的元素个数:"<<v1.size()<<endl;
cout<<"v1 容器的大小:"<<v1.capacity()<<endl;
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
cout<<"尾部插入5个元素后:"<<endl;
cout<<"v1 的元素个数:"<<v1.size()<<endl;
cout<<"v1 容器的大小:"<<v1.capacity()<<endl;
//vector 带参构造函数
//vector<int> v2(10); //构造时就分配空间,同时插入10个元素,元素值为0
vector<int> v2(10, 666);
//vector<int> v3(v2);
//vector<int> v3(v2.begin()+3, v2.end());
int test[]={1, 2, 3, 4, 5};
vector<int> v3(test, test+2); //注意左开右闭
cout<<"v2 的元素个数:"<<v2.size()<<endl;
cout<<"v2 容器的大小:"<<v2.capacity()<<endl;
cout<<"v2调用 assign 后:"<<endl;
cout<<"v2 的元素个数:"<<v2.size()<<endl;
cout<<"v2 中存储的元素是: "<<endl;
for(int i=0; i<v2.size(); i++){
cout<<v2[i]<<endl;
}
cout<<"v3 中存储的元素是: "<<endl;
for(int i=0; i<v3.size(); i++){
cout<<v3[i]<<endl;
}
}
void main(){
demo1();
system("pause");
return ;
}
注意:
- size的大小一定小于等于capacity,因为当插入一个元素时,容器空间不可能就刚好分配一个空间,这样的话频繁插入元素就要频繁分配内存,会很低效。所以容器在元素插入时,会预先分配一片空间(用内置算法分配)。
- 当我们使用vector的默认构造函数时,切记,在还没插入元素前,不能直接通过下标去访问,因为容器还没分配空间。
- 注意vector的有参构造的几种写法。
③vector的赋值
vector 的赋值
v2.assign(2, 888);//第一种玩法 改变原来vector 中的元素个数和值
v2.assign(v3.begin(), v3.end());//第二种玩法,使用迭代器重新赋值
int test1[]={1, 2, 3, 4, 5};
v2.assign(test1, test1+3);//第三种玩法,使用指针赋值
v2 = v3;//第四种玩法,赋值运算
④vector的大小
vector.size(); //返回容器中元素的个数
vector.empty(); //判断容器是否为空
vector.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
vector.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
⑤vector末尾的添加移除操作
v2.push_back(1); //在容器尾部加入一个元素
v2.pop_back(); //移除容器中最后一个元素
⑥vector的数据存取
第一 使用下标操作 v2[0] = 100;
第二 使用at 方法 如: v2.at(2) = 100;
第三 接口返回的引用 v2.front() 和 v2.back()
注意: 第一和第二种方式必须注意越界
⑦vector的插入
vector.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
vector.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
注意:涉及到位置一定是迭代器,而不是具体的数值。插入这种方式效率较低。
⑧vector的删除
-
删除整个vector
v2.clear();
cout<<“调用 v2.clear() 后”<<endl; -
删除单个元素
v2[1] = 888;
v2.erase(v2.begin()+1); -
删除多个元素(左闭右开)
v2.erase(v2.begin(), v2.begin()+3);
注意:这里的删除只是擦除,空间还是在那里。
如果要擦除空间,需要用到remove函数。
补充:
逆向迭代器:rbegin、rend可以逆向输出数据。
2.deque容器
①deque容器概念
deque是“double-ended queue”的缩写,和vector一样都是STL的容器,唯一不同的是:
deque是双端数组,而vector是单端的。
Deque 特点:
- deque在接口上和vector非常相似,在许多操作的地方可以直接替换。
- deque可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法),也可以用迭代器。
- deque头部和尾部添加或移除元素都非常快速, 但是在中部安插元素或移除元素比较费时。
使用时,包含头文件:#include < deque>
deque其实是多个数组组成的,比如在头部插入时,其可能在前面又增加了一个数组(扩容)。
②deque对象的默认构造
deque也是采用模板类实现。
deque对象的默认构造形式:deque< T> deqT
例如:
deque < int> deqInt; //存放int的deque容器。
deque < float> deqFloat; //存放float的deque容器。
deque < student> deqStu; //存放student类的deque容器。
…
注意:尖括号内还可以设置指针类型或自定义类型。
③deque对象的带参数构造
方式1:deque(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。
方式2:deque(n,elem); //构造函数将n个elem拷贝给本身。
方式3:deque(const deque &deq); //拷贝构造函数。
deque< int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deque< int> deqIntB(deqIntA.begin(),deqIntA.end()); //1 2 3 4
deque< int> deqIntC(8, 666); //8 8 8 8 8
deque< int> deqIntD(deqIntA); //1 2 3 4
④deque头部和末尾的添加移除操作
deque.push_back(element); //容器尾部添加一个数据
deque.push_front(element); //容器头部插入一个数据
deque.pop_back(); //删除容器最后一个数据
deque.pop_front(); //删除容器第一个数据
deque< int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
deqIntA.push_back(6);
deqIntA.pop_front();
deqIntA.pop_front();
deqIntA.push_front(7);
deqIntA.push_front(8);
deqIntA.pop_back();
deqIntA.pop_back();
deqIntA 中剩余元素: 8 7 3 4
⑤deque的数据存取
第一 使用下标操作 deqIntA[0] = 100;
第二 使用at 方法 如: deqIntA.at(2) = 100;
第三 接口返回的引用 deqIntA.front() 和 deqIntA.back()
注意: 第一和第二种方式必须注意越界
例如:
deque< int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
int i1 = deqIntA.at(0); //i1 = 1
int i2 = deqIntA[1]; //i2 = 2
deqIntA.at(0) = 666; //第一个元素改成666
deqIntA[1] = 888; //第二个元素改成888
int iFront = deqInt.front(); //666
int iBack = deqInt.back(); //5
deqInt.front() = 888; //第一个元素改成 888
deqInt.back() = 666; //最后一个元素改成 666
⑥deque与迭代器
deque.begin(); //返回容器中第一个元素的迭代器。
deque.end(); //返回容器中最后一个元素之后的迭代器。
deque.rbegin(); //返回容器中倒数第一个元素的迭代器。
deque.rend(); //返回容器中倒数最后一个元素之后的迭代器。
deque.cbegin(); //返回容器中第一个元素的常量迭代器。
deque.cend(); //返回容器中最后一个元素之后的常量迭代器。
deque< int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
//普通迭代器
for(deque< int>::iterator it = deqIntA.begin(); it!=deqIntA.end(); ++it){
(*it)++; //*it++ (*it)++注意区别,前者是指针的自增,后者是值的自增
cout<<*it;
cout<<" ";
}
//常量迭代器
deque< int>::const_iterator cit = deqIntA.cbegin();
for( ; cit!=deqIntA.cend(); cit++){
cout<<*cit;
cout<<" ";
}
//逆转的迭代器
for(deque<int>::reverse_iterator rit=deqIntA.rbegin(); rit!=deqIntA.rend(); ++rit){
cout<<*rit;
cout<<" ";
}
⑦deque的赋值
deque.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
deque.assign(n,elem); //将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符
deque.swap(deq); // 将deque与本身的元素互换
例如:
deque< int> deqIntA,deqIntB,deqIntC,deqIntD;
deque< int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
deqIntB.assign(deqIntA.begin(),deqIntA.end()); // 1 2 3 4 5
deqIntC.assign(4,888); //888 888 888 888
deqIntD = deqIntA; //1 2 3 4 5
deqIntC.swap(deqIntD); //互换
⑧deque的大小
deque.size(); //返回容器中元素的个数
deque.empty(); //判断容器是否为空
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
int iSize = deqIntA.size(); //5
deqIntA.resize(7); //1 2 3 4 5 0 0
deqIntA.resize(8,1); //1 2 3 4 5 0 0 1
deqIntA.resize(2); //1 2
⑨deque的插入
deque.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据 的位置。
deque.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
deque.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
#include <deque>
#include <iostream>
using namespace std;
int main(void){
deque<int> deqIntA;
deque<int> deqIntB;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntB.push_back(11);
deqIntB.push_back(12);
deqIntB.push_back(13);
deqIntB.push_back(14);
deqIntA.insert(deqIntA.begin(), 0); // {0,1,2,3,4}
deqIntA.insert(deqIntA.begin()+1, 2, 88); //{0,88,88,1,2,3,4}
deqIntA.insert(deqIntA.begin(), deqIntB.rbegin(), deqIntB.rend());{11,12,13,14,0,88,88,1,2,3,4}
for(deque<int>::iterator it = deqIntA.begin(); it!=deqIntA.end(); ++it){
cout<<*it;
cout<<" ";
}
system("pause");
}
⑩deque的删除
deque.clear(); //移除容器的所有数据
deque.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
deque.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
注意:clear和erase都不会释放空间。
#include <deque>
#include <iostream>
using namespace std;
int main(void){
deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
//方式一 单独使用擦除的接口
//deqIntA.erase(deqIntA.begin()+1); //干掉第二个元素 {1,3,4,5}
//deqIntA.erase(deqIntA.begin()+1, deqIntA.begin()+3);// 干掉3 和4, 剩下{1, 5}
//deqIntA.clear(); //干掉所有的元素
//方式二 使用迭代器遍历删除
for(deque<int>::iterator it = deqIntA.begin(); it!=deqIntA.end();){
if(*it == 4){
it = deqIntA.erase(it); //擦除后,迭代器指向原地,否则会乱套
}else {
cout<<*it;
cout<<" ";
it++;
}
}
system("pause");
}
3.List容器
①List 容器概念
list是一个双向链表容器,可高效地进行插入删除元素。
List 特点:
list不可以随机存取元素,所以不支持at.(position)函数与[]操作符。可以对其迭代器执行++,但是不能这样操作迭代器:it+3
使用时包含 #include < list>
②list对象的默认构造
list同样采用模板类实现,对象的默认构造形式:list< T> listT; 如:
list< int> lstInt; //定义一个存放int的list容器。
list< float> lstFloat; //定义一个存放float的list容器。
list< string> lstString; //定义一个存放string的list容器。
…
注意:尖括号内还可以设置指针类型或自定义类型。
③list对象的带参数构造
方式一:list(beg,end); //将[beg, end)区间中的元素拷贝给本身。
方式二:list(n,elem); //构造函数将n个elem拷贝给本身。
方式三:list(const list &lst); //拷贝构造函数。
list< int> lstInt1;
lstInt1.push_back(1);
lstInt1.push_back(2);
lstInt1.push_back(3);
list< int> lstInt2(lstInt1.begin(),lstInt1.end()); //1 2 3
list< int> lstInt3(5,8); //8 8 8 8 8
list< int> lstInt4(lstIntA); //1 2 3
④list头尾的添加移除操作
list.push_back(elem); //在容器尾部加入一个元素
list.pop_back(); //删除容器中最后一个元素
list.push_front(elem); //在容器开头插入一个元素
list.pop_front(); //从容器开头移除第一个元素
list< int> lstInt;
lstInt.push_back(1);
lstInt.push_back(2);
lstInt.push_back(3);
lstInt.push_back(4);
lstInt.push_back(5);
lstInt.pop_front();
lstInt.pop_front();
lstInt.push_front(11);
lstInt.push_front(12);
lstInt.pop_back();
lstInt.pop_back();
// lstInt {12, 11, 3}
⑤list的数据存取
list.front(); //返回第一个元素。
list.back(); //返回最后一个元素。
list< int> lstInt;
lstInt.push_back(1);
lstInt.push_back(2);
lstInt.push_back(3);
lstInt.push_back(4);
lstInt.push_back(5);
int iFront = lstInt.front(); //1
int iBack = lstInt.back(); //5
lstInt.front() = 11; //11
lstInt.back() = 19; //19
⑥list与迭代器
list.begin(); //返回容器中第一个元素的迭代器。
list.end(); //返回容器中最后一个元素之后的迭代器。
list.rbegin(); //返回容器中倒数第一个元素的迭代器。
list.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
list.cbegin(); //返回容器中第一个元素的常量迭代器。
list.cend(); //返回容器中最后一个元素之后的常量迭代器。
list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(3);
lstInt.push_back(5);
lstInt.push_back(7);
lstInt.push_back(9);
//普通迭代器
for (list<int>::iterator it=lstInt.begin(); it!=lstInt.end(); ++it)
{
cout << *it;
cout << " ";
}
//反转迭代器
for (list<int>::reverse_iterator rit=lstInt.rbegin(); rit!=lstInt.rend(); ++rit)
{
cout << *rit;
cout << " ";
}
⑦list的赋值
list.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。
list.assign(n,elem); //将n个elem拷贝赋值给本身。
list& operator=(const list &lst); //重载等号操作符。
list.swap(lst); // 将lst与本身的元素互换。
llist< int> lstIntA,lstIntB,lstIntC,lstIntD;
lstIntA.push_back(1);
lstIntA.push_back(3);
lstIntA.push_back(5);
lstIntA.push_back(7);
lstIntA.push_back(9);
lstIntB.assign(lstIntA.begin(),lstIntA.end()); //1 3 5 7 9
lstIntB.assign(++lstIntA.begin(),- -lstIntA.end()); //3 5 7
lstIntC.assign(5,8); //8 8 8 8 8
lstIntD = lstIntA; //1 3 5 7 9
lstIntC.swap(lstIntD); //互换
⑧list的大小
list.size(); //返回容器中元素的个数
list.empty(); //判断容器是否为空
list.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
list.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
注意:list因为是链表,不是连续存储,所以不需要预先分配空间,所以没有capacity这个方法。
list< int> lstIntA;
lstIntA.push_back(1);
lstIntA.push_back(2);
lstIntA.push_back(3);
if (!lstIntA.empty())
{
int iSize = lstIntA.size(); //3
lstIntA.resize(5); //1 2 3 0 0
lstIntA.resize(7,1); //1 2 3 0 0 1 1
lstIntA.resize(5); //1 2 3 0 0
}
⑨list的插入
list.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
list.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
list.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
list<int> listA;
list<int> listB;
listA.push_back(1);
listA.push_back(2);
listA.push_back(3);
listA.push_back(4);
listA.push_back(5);
listB.push_back(11);
listB.push_back(12);
listB.push_back(13);
listB.push_back(14);
listA.insert(listA.begin(), -1); //{-1, 1, 2, 3, 4, 5}
listA.insert( ++listA.begin(), 2, -2); //{-1, -2, -2, 1, 2, 3, 4, 5}
listA.insert(listA.begin() , listB.begin() , listB.end()); //{11, 12, 13, 14, -1, -2, -2, 1, 2, 3, 4, 5}
for(list<int>::iterator it = listA.begin(); it!=listA.end(); it++)
{
cout<< *it<<endl;
}
⑩list的删除
list.clear(); //移除容器的所有数据
list.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
list.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
list.remove(elem); //删除容器中所有与elem值匹配的元素。
#include <list>
#include <vector>
#include <iostream>
using namespace std;
int main(void){
//list 删除元素
list<int> listA;
listA.push_back(1);
listA.push_back(2);
listA.push_back(3);
listA.push_back(4);
listA.push_back(5);
//erase 的用法
list<int>::iterator itBegin=listA.begin();
++ itBegin;
list<int>::iterator itEnd=listA.begin();
++ itEnd;
++ itEnd;
++ itEnd;
listA.erase(itBegin,itEnd);//此时容器lstInt包含按顺序的1, 4, 5三个元素。
listA.erase(listA.begin());//此时容器lstInt包含按顺序的4, 5三个元素。
listA.push_back(4); // 4, 5, 4
listA.insert(listA.end(), 5, 4); //4, 5, 4, 4, 4, 4, 4, 4
/*remove 删除元素*/
//方式一 直接调用remove 方法
//listA.remove(4);
//方式二 遍历然后逐个删除
for(list<int>::iterator it=listA.begin(); it!=listA.end(); ){
if(*it == 4){
it =listA.erase(it); //相当于执行了++
}else {
it++;
}
}
for (list<int>::iterator it=listA.begin(); it!=listA.end(); ++it)
{
cout << *it;
cout << " ";
}
system("pause");
return 0;
}
①①list的反序排列
list.reverse(); //反转链表,比如list包含1, 2, 3, 4, 5五个元素,运行此方
法后,list就包含5, 4, 3, 2, 1元素。
list<int> listA;
listA.push_back(1);
listA.push_back(2);
listA.push_back(3);
listA.push_back(4);
listA.push_back(5);
listA.reverse(); //5, 4, 3, 2, 1
C++11新特性 变参模板、完美转发和emplace与insert的区别
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
#include <iostream>
using namespace std;
#include <vector>
#include <list>
#include <deque>
#include <algorithm>
class student {
public:
student() {
cout << "无参构造函数被调用!" << endl;
}
student(int age, string name, int test) {
this->age = age;
//strncpy_s(this->name, name, 64);
cout << "有参构造函数被调用!" << endl;
cout << "姓名:" << name.c_str() << " 年龄:" << age << endl;
}
student(const student &s) {
this->age = s.age;
//strncpy_s(this->name, s.name, 64);
cout << "拷贝构造函数被调用!" << endl;
}
~student() {
cout << "析构函数被调用" << endl;
}
public:
int age;
string name;
};
int main(void) {
//vector<int> vectInt(10);
deque<int> dqInt;
list<int> lstInt;
vector<student> vectStu(10);
cout << "vectStu size:" << vectStu.size() << endl;
cout << "vectStu capacity:" << vectStu.capacity() << endl;
//插入学生
//方法一 先定义对象,再插入(调用有参构造函数和拷贝构造函数)
//student xiaoHua(18, "李校花");
//vectStu.push_back(xiaoHua);
//方法二 直接插入临时对象(调用有参构造函数和拷贝构造函数,并且有参构造函数生成的临时变量在拷贝完后就会释放,因为生命周期就在括号之内)
//vectStu.push_back(student(19, "王大锤"));
//以上两种方法都会调用拷贝构造函数,会有效率的问题。
//c++11 新特性: 变参模板和完美转发
vectStu.emplace_back(19, "王大锤", 11); //效果与push_back一样,但是不会调用拷贝构造函数,即直接调用有参构造函数,然后插入到容器中
cout << "vectStu size (1):" << vectStu.size() << endl;
cout << "vectStu capacity(1):" << vectStu.capacity() << endl;
vectStu.emplace(vectStu.end(), 18, "lixiaohua", 12); //相当于 insert.注意如果插入到前面,那么会调用拷贝构造函数,因为插入会导致元素的移动
cout << "vectStu size (2):" << vectStu.size() << endl;
cout << "vectStu capacity (2):" << vectStu.capacity() << endl;
system("pause");
return 0;
}
变参模板是指,会根据传递的参数与构造函数的参数进行匹配。
完美转发是指,编译器根据写的参数去转发给对应的构造函数进行有参构造,构造对象。
注意上述:首先申请了容量为10的动态数组,之后准备插入“王大锤”时因为容量不够,所以会申请一片更大的容量,比如15。然后进行10次的拷贝构造和析构,目的就是把之前的数组的元素都放过来,然后再在插入“王大锤”时就只会调用有参构造了,再插入lixiaohua因为次数数组容量足够,也不会发生扩容移植,直接就是有参构造。
4.Set和multiset容器
①set/multiset容器概念
set和multiset是一个集合容器,其中set所包含的元素是唯一的,集合中的元素按一定的顺序排列。set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。在n个数中查找目标数的效率是 log2 n
红黑树定义 — 是每个节点都带有颜色属性(颜色为红色或黑色)的自平衡二叉查找树,满足下列性质:
1)节点是红色或黑色;
2)根节点是黑色;
3)所有叶子节点都是黑色节点(NULL);
4)每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
5)从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
Set 和 multiset 特点:
set中元素插入过程是按排序规则插入,所以不能指定插入位置。
set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。 如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素
头文件 #include < set>
// demo 15-34
#include <set>
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
int main(void) {
set<int> setInt;
multiset<int> msetInt;
multiset<int> msetInt1(msetInt.begin(), msetInt.end());
for(int i=0; i<10; i++){
msetInt.insert(100-i);
}
//set 不允许插入相同的元素,而multiset 是支持插入多个相同元素的.
msetInt.insert(99);
multiset<int>::iterator it = msetInt.begin();
for( ; it!=msetInt.end(); it++){
cout<<*it;
cout<<" ";
}
cout<<endl;
system("pause");
return 0;
}
②set/multiset对象的默认构造
set< int> setInt; //一个存放int的set容器。
set< float> setFloat; //一个存放float的set容器。
set< string> setString; //一个存放string的set容器。
multiset< int> mulsetInt; //一个存放int的multi set容器。
multiset< float> multisetFloat; //一个存放float的multi set容器。
multiset< string> multisetString; //一个存放string的multi set容器。
③Set/multiset 对象的带参构造函数
set(beg,end); //将[beg, end)区间中的元素拷贝给本身。
set(const set &s); //拷贝构造函数。
multiset(beg,end); //将[beg, end)区间中的元素拷贝给本身。
multiset(const multiset &s); //拷贝构造函数。
注意:没有构造函数将n个elem拷贝给本身这个方法。
④set对象的拷贝构造与赋值
set(const set &st); //拷贝构造函数
set& operator=(const set &st); //重载等号操作符
set.swap(st); //交换两个集合容器
setIntA.insert(5);
set<int> setIntA;
setIntA.insert(1);
setIntA.insert(2);
setIntA.insert(3);
setIntA.insert(4);
set< int> setIntB(setIntA); //1 2 3 4 5
set< int> setIntC;
setIntC = setIntA; //1 2 3 4 5
setIntC.insert(6); //1 2 3 4 5 6
setIntC.swap(setIntA); //交换
⑤仿函数(函数对象)functor的用法
Set/multiset 排序刨根究底:
定义一个student类,实现有参构造函数、大小于重载。
set容器用less和greater函数对象实现比较,为排序提供依据。
// demo 15-35
#include <set>
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
class student {
public:
student(int age) {
this->age = age;
}
bool operator < (const student &right) const{
return this->age < right.age;
}
bool operator > (const student &right) const{
return this->age > right.age;
}
int getAge() const { return age; } //需要常量限定
private:
int age;
string name;
};
int main(void) {
//less函数对象实现比较,为排序提供依据,是默认设定的
//set<int,less<int>> set1;
set<int,greater<int>> set1;
for(int i=5; i>0; i--){
set1.insert(i);
}
set<student> setStu; //等同于 set<student,less<student>>
setStu.insert(student(18));
setStu.insert(student(19));
/*for (set<int,greater<int>>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << *it << endl;
}*/
for (set<student>::iterator it = setStu.begin(); it != setStu.end(); it++) {
cout << it->getAge() ;
cout << " ";
}
system("pause");
return 0;
}
仿函数概念
尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。
functor,翻译成函数对象,伪函数,它是是重载了“()”操作符的普通类对象。 从语法上讲,它与普通函数行为类似。
functional头文件中包含的 greater<>与less<>就是函数对象。
下面举出greater< int> 和 less< int>的简易实现原理。
struct greater
{
bool operator() (const int& iLeft, const int& iRight)
{
return (iLeft>iRight);
}
}
struct less
{
bool operator() (const int& iLeft, const int& iRight)
{
return (iLeft<iRight);
}
}
set/setmulti容器就是调用函数对象的operator()方法去比较两个值的大小。
实现FunStudent普通类对象,其中实现()重载,实现仿函数。
#include <set>
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
class student {
public:
student(int age) {
this->age = age;
}
//从小到大
bool operator < (const student &right) const{
return this->age < right.age;
}
int getAge() const { return age; }
private:
int age;
string name;
};
class FunStudent{
public:
bool operator () (const student &left, const student &right){
cout<<"调用了 FunStudent ."<<endl;
ret = left.getAge() < right.getAge();//从小到大排列,如果是对象比较则调用重载函数
return ret; //当然有的编译器不支持ret,会有丢失一些 const-volatile 限定符的错误。
}
public:
int ret;
};
int main(void) {
//less 函数对象实现比较,为排序提供依据
//less 和greater 都是函数对象,有叫仿函数
//set<int,less<int>> set1;
set<int,greater<int>> set1;
for(int i=5; i>0; i--){
set1.insert(i);
}
//less<student>
set<student, FunStudent> setStu; //等同于 set<student,less<student>>
student lixiaohua(18);
student wangdachui(19);
//函数对象(仿函数)可以像函数一样直接调用
FunStudent funStu;
funStu(lixiaohua, wangdachui);
cout<<"比较结果:"<<funStu.ret<<endl;
setStu.insert(lixiaohua);
setStu.insert(wangdachui);
for (set<student, FunStudent>::iterator it = setStu.begin(); it != setStu.end(); it++) {
cout << it->getAge() ;
cout << " ";
}
system("pause");
return 0;
}
⑥set的插入和pair的用法
pair表示一个对组,它将两个值视为一个单元,把两个值捆绑在一起。
pair<T1,T2>用来存放的两个值的类型,可以不一样,也可以一样,如T1为int,T2为float。T1,T2也可以是自定义类。
pair.first是pair里面的第一个值,是T1类型。
pair.second是pair里面的第二个值,是T2类型。
set< int> setInt;
for(int i=5; i>0; i--){
//insert成功或失败返回的不是bool而是pair类型
pair<set<int>::iterator, bool> ret = setInt.insert(i);
if(ret.second){
cout<<"插入 "<<i<<" 成功!"<<endl;
}else {
cout<<"插入 "<<i<<" 失败!"<<endl;
}
}
注意:insert成功或失败返回的不是bool而是pair类型
⑦set与迭代器
set.insert(elem); //在容器中插入元素。
set.begin(); //返回容器中第一个数据的迭代器。
set.end(); //返回容器中最后一个数据之后的迭代器。
set.rbegin(); //返回容器中倒数第一个元素的迭代器。
set.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
set<int> setInt;
setInt.insert(3);
setInt.insert(4);
setInt.insert(1);
setInt.insert(5);
setInt.insert(2);
//顺序输出 1 2 3 4 5
for(set<int>::iterator it=setInt.begin(); it!=setInt.end(); ++it)
{
int elem = *it;
cout << elem; //或直接使用cout << *it
}
set.rbegin()与set.rend()。
⑧set/multiset的大小
set.size(); //返回容器中元素的数目
set.empty();//判断容器是否为空
注意事项: 它们没有resize 方法
set< int> setIntA;
setIntA.insert(3);
setIntA.insert(1);
setIntA.insert(7);
setIntA.insert(5);
setIntA.insert(9);
if (!setIntA.empty())
{
int iSize = setIntA.size(); //5
}
⑨set/multiset的删除
set.clear(); //清除所有元素
set.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
set.erase(beg,end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器。
set.erase(elem); //删除容器中值为elem的元素。
再次强调注意:erase返回的是下一个元素迭代器。
- 删除区间内的某个或某些元素
setInt是用set< int>声明的容器,假设它内部现已包含按顺序的1, 2, 3, 4, 5, 6元素。
set< int>::iterator itBegin=setInt.begin();
++ itBegin;
set< int>::iterator itEnd=setInt.begin();
++ itEnd;
++ itEnd;
++ itEnd;
setInt.erase(itBegin,itEnd);
//此时容器setInt包含按顺序的1, 4, 5, 6四个元素。
- 删除容器中第一个元素
setInt.erase(setInt.begin()); //4, 5, 6
- 删除容器中值为5的元素
setInt.erase(5); //4, 6
- 删除setInt的所有元素
setInt.clear(); //容器为空
⑩set/multiset的查找
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)。以上函数返回两个迭代器,而这两个迭代器被封装在pair中。
set<int> setInt;
setInt.insert(1);
setInt.insert(2);
setInt.insert(3);
setInt.insert(4);
setInt.insert(5);
set<int>::iterator it1 = setInt.find(4);
int elem1 = *it1; //elem1 == 4
int iCount = setInt.count(3); //iCount == 1
set<int>::iterator it2 = setInt.lower_bound(3);
set<int>::iterator it3 = setInt.upper_bound(3);
int elem2 = *it2; //i2 == 3
int elem3 = *it3; //i3 == 4
pair< set<int>::iterator, set<int>::iterator > pairIt = setInt.equal_range(3);
cout << *(pairIt.first) << endl; //3
cout << *(pairIt.second) << endl; //4
5.Map和multimap容器
①map/multimap的简介
map是标准的关联式容器,一个map里存储的元素是一个键值对序列,叫做(key,value)键值对。它提供基于key快速检索数据的能力。
- map中key值是唯一的。集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
- map底层的具体实现是采用红黑树变体的平衡二叉树的数据结构。在插入操作、删除和检索操作上比vector快很多。
- map可以直接存取key所对应的value,支持[]操作符,如map[key]=value。
- #include < map>
multimap与map的区别:
map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
#include <map>
#include <iostream>
#include <functional>
#include <algorithm>
#include <string>
using namespace std;
int main(void) {
multimap<int, string> mapStu;
mapStu.insert(pair<int, string>(1, "张三"));
mapStu.insert(pair<int, string>(2, "李四"));
mapStu.insert(pair<int, string>(3, "王五"));
//multimap 不支持[]操作,map 支持
//mapStu[4] = "赵六";
//multimap 支持相同的key 插入
mapStu.insert(pair<int, string>(3, "小王五"));
for(multimap<int, string>::iterator it=mapStu.begin(); it!=mapStu.end(); it++){
cout<<"key: "<<(*it).first << " value: "<<(*it).second <<endl;
}
system("pause");
return 0;
}
②map/multimap对象的默认构造
map/multimap采用模板类实现,对象的默认构造形式:
map<T1,T2> mapTT;
multimap<T1,T2> multimapTT;
如:
map<int, char> mapA;
map<string,float> mapB;
//其中T1,T2还可以用各种指针类型或自定义类型
③map和multimap对象的带参数构造
方式一:map(beg,end); //将[beg, end)区间中的元素拷贝给本身。
方式二:map(const map &mapObject); //拷贝构造函数。
④map的插入与迭代器
map.insert(…); //往容器插入元素,返回pair<iterator,bool>
map中插入元素的四种方式:
假设 map<int, string> mapStu;
方式一、通过pair的方式插入对象
mapStu.insert( pair<int,string>(1,“张三”) );
方式二、通过pair的方式插入对象
mapStu.inset(make_pair(2, “李四”));
方式三、通过value_type的方式插入对象
mapStu.insert( map<int,string>::value_type(3,“王五”) );
方式四、通过数组的方式插入值
mapStu[4] = “赵六”;
mapStu[5] = “小七";
注意:
前三种方法,采用的是insert()方法,该方法返回值为pair<iterator,bool>
第四种方法非常直观,但碰到相同的键时会进行覆盖操作。比如插入key 为4的键值时,先在mapStu中查找主键为4的项,若不存在,则将一个键为4,值为默认初始化值的对组插入到mapStu中,然后再将值修改成“赵六”。若发现已存在4这个键,则修改这个键对应的value。
string strName = mapStu[8]; //取值操作或插入操作
只有当mapStu存在8这个键时才是正确的取操作,否则会自动插入一个实例,键为8,值为默认构造时的初始化值。
迭代器
map.begin(); //返回容器中第一个数据的迭代器。
map.end(); //返回容器中最后一个数据之后的迭代器。
map.rbegin(); //返回容器中倒数第一个元素的迭代器。
map.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
#include <map>
#include <iostream>
#include <functional>
#include <algorithm>
#include <string>
using namespace std;
int main(void) {
map<int, string> mapStu;
//方式一 构造一个pair ,然后插入
pair<map<int, string>::iterator,bool> ret = mapStu.insert(pair<int, string>(1, "张三"));
if(ret.second==true){
cout<<"插入成功! value: "<<(*(ret.first)).second<<endl;
}else {
cout<<"插入失败!"<<endl;
}
//如果键存在,则插入会失败
ret = mapStu.insert(pair<int, string>(1, "小张三"));
if(ret.second==true){
cout<<"插入成功! value: "<<(*(ret.first)).second<<endl;
}else {
cout<<"插入小张三失败! "<<endl;
}
//方式二 使用make_pair
mapStu.insert(make_pair(2, "李四"));
//方式三 使用value_type, 相当于pair<int, sting>
mapStu.insert(map<int, string>::value_type(3, "王五"));
//方式四 直接使用[]重载,如果键值对已经存在,则覆盖原值
mapStu[4]="赵六";
mapStu[4] = "小赵六";
mapStu[5] = mapStu[6]; //不存在则值为默认初始化值
mapStu[7] = mapStu[4];
for(map<int, string>::iterator it=mapStu.begin(); it!=mapStu.end(); it++){
cout<<"key: "<<(*it).first << " value: "<<(*it).second <<endl;
}
system("pause");
return 0;
}
⑤map/multimap 排序
map<T1,T2,less< T1> > mapA; //该容器是按键的升序方式排列元素。未指定函数对象,默认采用less< T1>函数对象。
map<T1,T2,greater< T1>> mapB; //该容器是按键的降序方式排列元素。
less< T1>与greater< T1> 可以替换成其它的函数对象functor。
可编写自定义函数对象以进行自定义类型的比较,使用方法与set构造时所用的函数对象一样。
⑥map对象的拷贝构造与赋值
map(const map &mp); //拷贝构造函数
map& operator=(const map &mp); //重载等号操作符
map.swap(mp); //交换两个集合容器
例如:
map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(1, "张三"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
map<int ,string> mapB(mapA); //拷贝构造,此时mapB 和mapA中元素一致
map<int, string> mapC;
mapC = mapA; //赋值,此时mapC 和mapA中元素一致
mapC[3] = "老张"; //mapC中,此时包含 张三, 李四, 老张, 赵六
mapC.swap(mapA); //mapA 和mapC交换
⑦map的大小
map.size(); //返回容器中元素的数目
map.empty();//判断容器是否为空
map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(1, "张三"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
if (!mapA.empty())
{
int size = mapA.size(); //size 为 4
}
⑧map的删除
map.clear(); //删除所有元素
map.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
map.erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
map.erase(key); //删除容器中key为key的对组,返回删除的对组个数
Map.erase(key_type *first, key_type *last) //删除数组指定的半闭半开的区间中特定的key对应的所有队组
size_type其实就是一个无符号整数。
map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(1, "张三"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
//删除区间内的元素,迭代器指示区间(半闭半开)
map<int,string>::iterator itBegin=mapA.begin();
++ itBegin;
map<int,string>::iterator itEnd=mapA.end();
mapA.erase(itBegin,itEnd); //此时容器mapA仅仅包含{1,"张三"}一个元素。
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
//删除容器中的第一个元素,使用迭代器指示位置
mapA.erase(mapA.begin()); //mapA包含{2,"李四"}{3,"王五"}{4,"赵六"}三个元素
//删除容器中key为4的元素
mapA.erase(4);
//删除mapA的所有元素
mapA.clear(); //容器为空
//int range[] = {1,3,4};
//mapStu.erase(range+1,range+2);
⑨map/multimap的查找
map.find(key); 查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
map.count(key); //返回容器中键值为key的对组个数。对map来说,要么是0,要么是1;对multimap来说,值>=0。
map.lower_bound(keyElem); //返回第一个key>=keyElem元素的迭代器。
map.upper_bound(keyElem); // 返回第一个key>keyElem元素的迭代器。
map.equal_range(keyElem); //**返回容器中key与keyElem相等的上下限的两个迭代器。**上限是闭区间,下限是开区间,如[beg,end)。
#include <map>
#include <iostream>
#include <functional>
#include <algorithm>
#include <string>
using namespace std;
int main(void) {
map<int, string> mapStu;
mapStu.insert(pair<int,string>(2, "李四"));
mapStu.insert(pair<int,string>(1, "张三"));
mapStu.insert(pair<int,string>(3, "王五"));
mapStu.insert(pair<int,string>(4, "赵六"));
multimap<int, string> mmapTeacher; //<班级,老师姓名>
mmapTeacher.insert(pair<int,string>(101, "李老师"));
mmapTeacher.insert(pair<int,string>(101, "张老师"));
mmapTeacher.insert(pair<int,string>(102, "王老师"));
mmapTeacher.insert(pair<int,string>(102, "赵老师"));
//map 的查找
map<int, string>::iterator it = mapStu.find(3);
if(it !=mapStu.end()){//找到了
cout<<"mapStu.find(3) = "<<(*it).second<<endl;
}else {//没找到
cout<<"找不到键值为3的键值对!"<<endl;
}
//multimap 的查找
int count = mmapTeacher.count(101);
multimap<int, string>::iterator mit = mmapTeacher.find(101);
if(mit !=mmapTeacher.end()){//找到了
//输出multimap 中的同一键的多个值
//方法一 通过比较key ,循环判断 推荐使用
/*for(; mit!=mmapTeacher.end(); mit++){
if((*mit).first == 101){
cout<<"mmapTeacher.find(101) = "<<(*mit).second<<endl;
}else{
break;
}
}*/
//方法二 通过count计数来控制
for(int i=0; i<count; i++, mit++){
cout<<"mmapTeacher.find(101) = "<<(*mit).second<<endl;
}
}else {//没找到
cout<<"找不到键值为101的键值对!"<<endl;
}
//equal_range 用法
pair<multimap<int, string>::iterator, multimap<int, string>::iterator> mmiit = mmapTeacher.equal_range(101);
//第一个迭代器, 对应begin
if(mmiit.first != mmapTeacher.end()){
cout<<"mmapTeacher.equal_range(101).begin ="<<(*mmiit.first).second<<endl;
}
//第二个迭代器,对应end
if(mmiit.second != mmapTeacher.end()){
cout<<"mmapTeacher.equal_range(101).end ="<<(*mmiit.second).second<<endl;
}
for(map<int, string>::iterator it=mapStu.begin(); it!=mapStu.end(); it++){
cout<<"key: "<<(*it).first << " value: "<<(*it).second <<endl;
}
system("pause");
return 0;
}
6.Queue容器
①Queue简介
queue是队列容器,是一种“先进先出”的容器。
默认情况下queue是利用deque容器实现的一种容器。
它只允许在队列的前端(front)进行删除操作,而在队列的后端(back)进行插入操作
#include < queue>
②queue对象的默认构造
queue采用模板类实现,queue对象的默认构造形式:queue< T> queT; 如:
queue< int> queueInt; //一个存放int的queue容器。
queue< float> queueFloat; //一个存放float的queue容器。
queue< string> queueString; //一个存放string的queue容器。
…
注意: 尖括号内还可以设置指针类型或自定义类型。
③queue 对象的带参构造
queue<int, list< int>> queueList; //内部使用list 来存储队列元素的queue 容器.
错误: queue<int, vector< int>> queueList; //内部不能使用vector来存储队列元素 ,不兼容(要支持pop_front、push_back的)
④queue的push()与pop()方法
queue.push(elem); //往队尾添加元素
queue.pop(); //从队头处移除队首元素
返回类型都是void。
queue<int> queueInt;
queueInt.push(1);
queueInt.push(2);
queueInt.push(3);
queueInt.push(4);
queueInt.pop();
queueInt.pop();
//此时queueInt存放的元素是3, 4
⑤queue对象的拷贝构造与赋值
queue(const queue &que); //拷贝构造函数
queue& operator=(const queue &que); //重载等号操作符
queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);
queue<int> queIntB(queIntA); //拷贝构造
queue<int> queIntC;
queIntC = queIntA; //赋值
⑥queue的数据存取
queue.back(); //返回最后一个元素
queue.front(); //返回第一个元素
queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);
int iFront = queIntA.front(); //1
int iBack = queIntA.back(); //5
queIntA.front() = 66; //66
queIntA.back() = 88; //88
⑦queue的大小
queue.empty(); //判断队列是否为空
queue.size(); //返回队列的大小
queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);
if (!queIntA.empty())
{
int iSize = queIntA.size(); //iSize = 5
}
7.优先级队列priority_queue
优先队列: 它的入队顺序没有变化,但是出队的顺序是根据优先级的高低来决定的。优先级高的优先出队。
最大值优先级队列、最小值优先级队列
用来开发一些特殊的应用
#include < queue>
#include <queue>
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <set>
using namespace std;
int main(void) {
//priority_queue<int> pqA;//默认情况下是值越大,优先级越大
//priority_queue<int, vector<int>, greater<int>> pqA; //使用 vector 值越小,优先级越大
priority_queue<int, deque<int>, greater<int>> pqA; //使用deque 值越小,优先级越大
//priority_queue<int, list<int>, greater<int>> pqA; //不可以使用list,不兼容
pqA.push(1);
pqA.push(2);
pqA.push(3);
pqA.push(3);
pqA.push(4);
pqA.push(5);
pqA.push(3);
while(!pqA.empty()){
cout<<pqA.top()<<" ";//读取队首的元素,但元素不出列
pqA.pop(); //出队列
}
cout<<endl;
system("pause");
return 0;
}
注意:
priority_queue< int> pqA;//默认情况下是值越大,优先级越大
priority_queue<int, vector< int>, greater< int>> pqA; //使用 vector 值越小,优先级越大
priority_queue<int, deque< int>, greater< int>> pqA; //使用deque 值越小,优先级越大
priority_queue<int, list< int>, greater< int>> pqA; //不可以使用list,不兼容
8.stack容器
stack是堆栈容器,是一种“先进后出”的容器。
stack是基于deque容器而实现的容器。
#include < stack>
①stack对象的默认构造
stack采用模板类实现, stack对象的默认构造形式: stack < T> stkT;
stack < int> stkInt; //一个存放int的stack容器。
stack < float> stkFloat; //一个存放float的stack容器。
stack < string> stkString; //一个存放string的stack容器。
…
//尖括号内还可以设置指针类型或自定义类型。
②stack的push()与pop()方法
stack.push(elem); //往栈头添加元素
stack.pop(); //从栈头移除第一个元素
stack<int> stkInt;
stkInt.push(1);
stkInt.push(2);
stkInt.pop();
stkInt.push(3);
此时stkInt存放的元素是1, 3
③stack对象的拷贝构造与赋值
stack(const stack &stk); //拷贝构造函数
stack& operator=(const stack &stk); //重载等号操作符
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);
stack<int> stkIntB(stkIntA); //拷贝构造
stack<int> stkIntC;
stkIntC = stkIntA; //赋值
④stack的数据存取
stack.top(); //返回最后一个压入栈元素
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);
int iTop = stkIntA.top(); //3
stkIntA.top() = 88; //88
⑤stack的大小
stack.empty(); //判断堆栈是否为空
stack.size(); //返回堆栈的大小
stack<int> stkInt;
stkInt.push(1);
stkInt.push(2);
stkInt.push(3);
int iSize = stkInt.size(); //3
9.Array容器(C++11 新增)
①array容器概念
- array是将元素置于一个固定数组中加以管理的容器。
- array可以随机存取元素,支持索引值直接存取, 用[]操作符或at()方法对元素进行操作,也可以使用迭代器访问
- 不支持动态的新增删除操作
- array可以完全替代C语言中的数组,使操作数组元素更加安全!
②array对象的构造
- array采用模板类实现,array对象的默认构造形式(涉及非类型参数-数值类模板)
- array<T,10> arrT; //10 为数值型模板参数
array<int, 6> a1; //一个存放int的array容器
array<float, 6> a2; //一个存放float的array容器
array<student, 6> a3; //一个存放student的array容器
array<int, 6> a1={1,2,3,4,5,6}; //定义时同时初始化
③array的赋值
a2.assign(0); //改变原来array中的所有元素的值,assign不建议用,编译器会报错,除非加上其提示的宏
a2.fill(666); //用特定值填充array中的所有元素
array<int, 6> a1 = {1, 2, 3}; //定义时同时初始化,或者定义后用初始化列表
array<int, 6> a2 ;
a2 = a1; // 赋值运算,将a1 赋值给a2(同类型同大小)
a1.swap(a2); //和其他array交换
④array的大小
array.size(); //返回容器中元素的个数
array.max_size(); //返回容器中最大的元素个数,与size 等同
array.empty(); //判断容器是否为空吗?永远返回false
⑤array的数据存取
第一 使用下标操作 a1[0] = 100;
第二 使用at 方法 如: a1.at(2) = 100;
第三 接口返回的引用 a2.front() 和 a2.back()
第四 返回内建数组的指针 a1.data()
注意: 第一和第二种方式必须注意越界
10.常见疑难杂症
注意:任何时候在模板(template)中使用一个嵌套从属类型名称, 需要在前一个位置, 添加关键字typename;
比如例中使用迭代器类型时,就要使用typename.虽然在vs2010 和vs2015中没有错误,但在VC++2019和gcc编译器中,都会报错。
如:typename list::const_iterator citor;
// demo 15_42_疑难杂症
#include <iostream>
#include <deque>
#include <string>
#include <vector>
#include <list>
#include <Windows.h>
using namespace std;
template <typename T>
void printInf(const list<T>& object) throw()
{
string line(50, '-');
typename list<T>::const_iterator citor;
for (citor = object.begin(); citor != object.end(); citor++) {
cout << *citor << endl;
}
cout << endl;
cout << "size:" << object.size() << endl;
cout << line << endl;
return;
}
class Student
{
public:
Student() {
cout << "默认构造函数" << endl;
this->m_nAge = 0;
this->m_sName = "未知";
}
Student(int _age, const char* _name) {
cout << "带参数的构造函数" << endl;
this->m_nAge = _age;
this->m_sName = _name;
}
Student(const Student& object) {
cout << "拷贝构造函数" << endl;
this->m_nAge = object.m_nAge;
this->m_sName = object.m_sName;
}
~Student() {
cout << "析构函数 " << endl;
}
friend ostream& operator<<(ostream& out, const Student& stu);
public:
string m_sName;
int m_nAge;
};
ostream& operator<<(ostream& out, const Student& stu) {
out << "年龄:" << stu.m_nAge << "\t" << "姓名:" << stu.m_sName;
return out;
}
int main(int agrc, char** argv)
{
Student s1(21, "张大帅");
Student s2(21, "李小美");
Student s3(51, "张三");
Student s4(50, "罗二");
list<Student> stuList;
printInf<Student>(stuList);
system("pause");
return 0;
}