1. 容器的概念
容器是用来批量存储数据的集合,数据元素可以是用户自定义类型,也可以是C++预定定义类型。容器类的对象自动申请和释放内存,无需new和delete操作。
容器:顺序容器 和关联容器
顺序容器:元素之间是顺序关系,元素有固定的位置
关联容器:元素之间没有严格物理上的顺序关系。内部是有联系的
顺序容器:Array数组,vector向量,list双向链表,deque
关联容器:map 键值对 ,mulitmap 多重键值对
容器分类如下图所示:
2. 容器的分类
2.1 顺序容器
是一种各个元素直接有顺序关系的线性容器,每个元素都有自己的位置,通过下标或迭代器进行访问,除非删除或者插入的数据改变元素的顺序。
本次学习的顺序容器有:array(数组)、string、vector(向量)、list(列表)、deque(队列)
2.1.1 array(数组)
C++11新增的数组,符合容器的设计规范,使用起来更加方便。
关于array的使用
#include <iostream>
#include <array>
using namespace std;
int main()
{
//创建一个长度为5的int数组
array<int,5> arr = {1,2,3};
//取出对应下标的元素值
cout << arr.at(0) << endl; //1
cout << arr[1] << endl; //2
cout << arr[4] << endl; //0
//更改第四个数据的值为884
arr[3] = 886;
cout << arr[3] << endl;
cout << "-----for------" << endl;
for(int i = 0; i<arr.size();i++)
{
cout << arr.at(i) << " ";
}
cout << endl;
//给所有元素填充一个固定值
arr.fill(521);
cout << "-----foreach------" << endl;
for(int i:arr)
{
cout << i << " ";
}
cout << endl;
return 0;
}
2.1.2 string
string是C++源码中自带的类,类可以包含很多成员函数等。
关于string类的函数个人整理后在下面代码中展示:
#include <iostream>
using namespace std;
int main()
{
//创建一个内容为空的字符串对象
string s;
cout << s.empty() << endl; //1
string s1 = "Monday"; //隐式调用构造函数
string s2("Monady"); //显示调用构造函数
cout << (s1==s2) << endl; //0
string s3(s2); //拷贝构造函数
s2 = s1; //赋值运算符
cout << s3 << " " << s2 << endl; //Monady Monday
//参数1:char*原字符串 参数2:从前往后保留字符数
string s4("ASDFF",2);
cout << s4 << endl; //AS
//参数1:string原字符串 参数2:从前往后不保留字符数
string s5(s2,2);
cout << s5 << endl; //nady
//参数1:字符数量 参数2:cahr字符内容
string s6(5,'A');
cout << s6 << endl; //AAAAA
//字符串拼接:+
string s7 = s4 + s6;
cout << s7 << endl; //ASAAAAA
//向后追加字符串:append()
s7.append("YYY").append("WUW");
cout << s7 << endl; //ASAAAAAYYYWUW
//向后追加单字符:push_back()
s7.push_back('p');
cout << s7 << endl; //ASAAAAAYYYWUWp
//交换字符串:swap()
swap(s5,s6);
cout << s5 << " " << s6 << endl; //AAAAA nday
//插入字符串:insert() 参数1:插入的位置 参数2:插入的内容
s7.insert(2,"****");
cout << s7 << endl; //AS****AAAAAYYYWUWp
//删除内容:erase() 参数1:删除的起止位置 参数2:删除的字符数
s7.erase(4,3);
cout << s7 << endl; //AS**AAAAYYYWUWp
//替换:replace() 参数1:替换的起始位置 参数2:替换的字符数 参数3:替换的内容
s7.replace(0,3,"++++");
cout << s7 << endl; //++++*AAAAYYYWUWp
//遍历:使用for即可
for(int i=0;i<s.size();i++)
{
cout<<s[i]<<" ";
}
cout<<endl;
//清空:clear()
s7.clear();
cout << s7.size() << endl; //0
return 0;
}
2.1.3 vector
vector内部使用数组实现,最常用的一种顺序容器,适合高效的进行随机存取,不适合插入和删除。可以理解为可变长度的数组,是一种顺序结构的线性表,故他的特性和线性表差不多,如随机访问;且插入删除的时间复杂度是O(n)。
关于vector指针的位置如图所示,我这里设置了一个最大长度为7里面包含5个元素的vector数组,其中first指针指向下标为0的元素,end指针指向最大长度的后一位。
故last-first即为该表存储的数量,end-first=size即为该表的长度。
当插入的数值超范围后,vector会另外开辟出一块更大的空间用于存储,转移的顺序如下:
1. 转移表内的元素信息
2. 转移first、end、last等指针
3. 删除原有的表,完成转移
由上可知,为确保程序运行的效率,故vector无直接插在首位的函数如:frontpush,后面的list容器可以解决这种问题。
我们现在将我所整理的函数及其功能列出来:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//创建一个长度为5向量对象
vector<int> vec(5);
cout << vec.size() << endl; //5
//取出元素值
cout << vec.at(0) << " " << vec[1] << endl; //0 0
//向后添加元素
vec.push_back(12);
cout << vec.size() << endl; //6 此时长度为6,动态扩充
//删除最后一个元素
vec.pop_back();
//在第二个位置插入元素222
vec.insert(vec.begin()+1,222);
//在倒数第二个位置插入元素4
vec.insert(vec.end()-1,4);
//删除倒数第一个元素
vec.erase(vec.end()-1);
cout << "-----for----" << endl;
for(int i=0;i<vec.size();i++)
{
cout << vec.at(i) << " ";
}
cout << endl;
//清空元素
vec.clear();
cout << vec.empty() << endl; //1
return 0;
}
2.1.4 list
list内部由双向链表实现,元素的内存空间不连续,因此适合高效的进行插入和删除操作,但是不适合使用下标操作(只能通过迭代器指针操作元素),也不适合大量的随机存取。
#include <iostream>
#include <list>
using namespace std;
int main()
{
//创建一个长度为4的列表对象,每个元素的初始值为“hello”
list<string> lis(4,"hello");
cout << lis.empty() << endl; //0
//向后追加
lis.push_back("hou");
//向前追加
lis.push_front("qian");
cout << lis.size() << endl; //6
//在第一个位置插入元素
lis.insert(lis.begin(),"frist");
//获取第一个元素
cout << lis.front() << endl; //frist
//在最后一个位置插入元素
lis.insert(lis.end(),"finish");
//获取最后一个元素
cout << lis.back() << endl; //finish
// 删除第一个元素
lis.pop_front();
cout << lis.front() << endl; //qian
//删除最后一个元素
lis.pop_back();
cout << lis.back() << endl; //hou
//第二个位置插入元素
//注:list的迭代器指针不支持+,可以使用自增或自减
lis.insert(++lis.begin(),"222");
//在倒数第二个位置插入元素
lis.insert(--lis.end(),"lala");
//在第五个位置插入元素
// 1、获取起始位置的迭代器指针 只读:const_iterator 可读写:iterator
list<string>::iterator iter = lis.begin();
//2、把迭代器指针向后移动4位(参数1:迭代器指针 参数2:向后移动的数量,负数为向前)
advance(iter,4);
//3、使用*取内容,即元素本身
cout << "lis[4]:" << *iter << endl; //hello
//4、插入元素"555"
lis.insert(iter,"555");
cout << "lis[4]:" << *iter << endl; //hello
//修改第五个元素
iter = lis.begin();
advance(iter,4);
*iter = "exo";
//删除第四个元素
iter = lis.begin();
advance(iter,3);
lis.erase(iter);
//排序:按照编码方式
// lis.sort();
//不支持for循环,但是支持foreach
for(string i:lis)
{
cout << i << " ";
}
cout << endl;
//清空
// lis.clear();
// cout << lis.empty() << endl; //1
return 0;
}
2.1.5 deque
从API上基本同时兼容vector和list,随机存取和插入删除的性能都介于vector和list直接,算是一种比较均衡 的顺序容器类型。相对而言,deque比较擅长首尾两端的存取操作。因为兼容的缘故deque可以直接使用vector和list二者的函数用法,注意使用时要换函数介绍为deque
2.2 关联容器
各个元素直接没有严格的物理顺序,但是在内部由一个排序特点,因此就可以使用迭代器进行遍历。
常见的关联容器有:map(键值对映射)、multimap(多重键值对映射)等
以map为例,来说明关联容器的使用。
2.2.1 map
map以键值对的方式来存储元素数据,map与multimap的区别仅仅在于一个键对应一个值还是一个键对应多个值,map更为常用。
键作为数据的名称,具有唯一性,而值则不必。
键通过为字符串类型,而值则根据存储的数据类型而定。
#include <iostream>
#include <map>
using namespace std;
int main()
{
//创建一个元素为空的map对象
map<string,int> m;
//插入元素
m["身高"] = 188;
m["体重"] = 50;
m["存款"] = 12000;
//如果这个键值对已经存在,则表示修改(键名改不了,只能修改值)
m["存款"] = 15000;
m.insert(pair<string,int>("年龄",27));
m.insert(pair<string,int>("工龄",4));
//在取出数据时应该先进行判断值是否存在,
if(m.find("存款") != m.end())
{
//取出键对应的值 键--first 值--second
cout << m.find("存款")->second << endl;
}else
{
cout << "存款键不存在" << endl;
}
if(m.find("住址") != m.end())
{
//取出键对应的值 键--first 值--second
cout << m.find("住址")->second << endl;
}else
{
cout << "住址键不存在" << endl;
}
//删除键值对
bool result = m.erase("体重");
cout << m.size() << endl; //4
if(result)
{
cout << "删除体重成功" << endl;
}else
{
cout << "删除体重失败" << endl;
}
result = m.erase("收入");
if(result)
{
cout << "删除收入成功" << endl;
}else
{
cout << "删除收入失败" << endl;
}
//清空
m.clear();
cout << m.empty() << endl; //1
return 0;
}
2.3 迭代器
C++中为每一个容器类都提供了对应的迭代器类型,用于遍历容器,迭代器是一个特殊的指针。
通常情况下,容器类型可以通过begin或end函数获得头部或尾部的跌大气,随后不断移动迭代器来取出所有的元素。
迭代器遍历的效率是各种方式中最高的。如果只读使用const_iterator,如果读写使用iterator。
#include <iostream>
#include <array>
#include <string>
#include <vector>
#include <list>
#include <deque>
#include <map>
using namespace std;
int main()
{
string str = "hello";
for(string::const_iterator iter = str.begin();iter!= str.end();iter++)
{
cout << *iter << " ";
}
cout << endl;
array<string,5> arr = {"aaa","bbb","ccc","ddd","eee"};
for(array<string,5>::const_iterator iter = arr.begin();iter!= arr.end();iter++)
{
cout << *iter << " ";
}
cout << endl;
vector<double> vec;
vec.push_back(11.2);
vec.push_back(12.2);
vec.push_back(13.2);
vec.push_back(14.2);
vec.push_back(15.2);
for(vector<double>::const_iterator iter = vec.begin();iter!= vec.end();iter++)
{
cout << *iter << " ";
}
cout << endl;
list<string> lis;
lis.push_back("AA");
lis.push_back("BB");
lis.push_back("CC");
lis.push_back("DD");
lis.push_back("EE");
for(list<string>::const_iterator iter = lis.begin();iter!= lis.end();iter++)
{
cout << *iter << " ";
}
cout << endl;
deque<int> deq;
deq.push_back(12);
deq.push_back(13);
deq.push_back(14);
deq.push_back(15);
deq.push_back(16);
for(deque<int>::const_iterator iter = deq.begin();iter!= deq.end();iter++)
{
cout << *iter << " ";
}
cout << endl;
map<string,string> m;
m["name"] = "Json";
m["age"] = "18";
m["sex"] = "man";
m["money"] = "10000";
m["car"] = "benchi";
for(map<string,string>::const_iterator iter = m.begin();iter != m.end();iter++)
{
cout << iter->first << " " << iter->second;
}
cout << endl;
return 0;
}