容器的增删改查也要小心点
1.介绍
在前面一篇文章中提到过,对于C++来说,一个容器就是一些特定类型对象的集合,顺序容器为我们提供了控制同一类元素的存储和访问顺序的能力。这种顺序与元素加入容器时的位置相对应。下面表1对C++中顺序容器的类型进行一个总结。
类型 | 底层数据结构 | 使用规则 |
---|---|---|
vector | 可变大小数组 | 支持快速随机访问,在尾部之外的位置插入或者删除元素会比较慢 |
deque | 双端队列 | 同样支持快速随机访问,由于队列的特性,deque在头尾部插入/删除元素的速度很快 |
list | 双向链表 | 支持双向顺序访问,list在任何位置进行修改(插入/删除)是很快的 |
forward_list | 单向链表 | 支持单向顺序访问,任何位置进行修改(插入/删除)是很快的 |
string | 可变大小数组 | 与vector十分相似,但是专门用于保存字符,随机访问快,在尾部进行增删快 |
array | 固定大小数组 | 支持快速随机访问,不能添加或者删除元素 |
- 增删改查是关于数据的最基本的操作,既然容器封装好了数据,那么就肯定提供了相关的增删改查库函数,但是由于不同顺序容器底层实现的一些区别,所以它们之间的的增删改查有时候并不相同。
2.添加元素
2.1 一般的增加元素
对于容器的添加而言,标准库为我们封装了两种通用函数,分别是push_back、push_front
以及emplace_front、emplace_back和insert
五种方法,顾名思义,其中push_back和emplace_back
就是在容器尾部进行增加元素,push_front和emplace_front
就是在容器头部进行增加元素,而insert就是在任意位置进行数据的插入。下面的表2对其做一个小总结,我们需要知道的是array是不允许进行元素添加操作的。
方法名 | 方法使用说明 | 返回值 | 适用性 |
---|---|---|---|
c.push_back(val) | 在容器c的尾部拷贝 创建一个值 为val的元素, |
void | forward_list容器不支持 |
c.push_front(val) | 在容器c的头部拷贝 创建一个值 为val的元素, |
void | vector和string容器不支持 |
c.emplace_back(args) | 在容器c的尾部创建一个由args构造 的元素, |
void | forward_list容器不支持 |
c.emplace_front(args) | 在容器c的头部创建一个由args构造 的元素, |
void | vector和string容器不支持 |
c.insert(p,t) | 在容器c迭代器p指向的元素之前创建一个值为t 的元素, |
新添加元素的迭代器 | forward_list有自己的插入函数 |
c.insert(p,n,t) | 在容器c迭代器p指向的元素之前创建n个值为t 的元素, |
第一个新添加元素的迭代器 | 向vector,string以及deque插入元素会导致迭代器失效 |
c.insert(p,b,e) | 在容器c迭代器p指向的元素之前插入迭代器p和e 之间的元素(左闭右开), |
第一个新添加元素的迭代器 | |
c.insert(p,lis) | 在容器c迭代器p指向的元素之前插入元素值列表lis 中的元素 |
第一个新添加元素的迭代器 |
上面对于添加元素的部分函数的不支持我们应该从其底层实现来把握;
- 首先对于数组实现的数据结构
vector
和string
,它们两个进行头部插入元素必然会因为元素的整体移动导致极大的开销,所以不支持push_front
类似的操作。 - 另外对于
forward_list
而言,它是一个单向链表,所以我们要对其进行类似push_back
的操作就会十分艰难,所以它也不支持类似操作。而且如果要对forward_list
进行插入的操作,必须在基于待插入位置前面的指针的基础上进行插入。 - 最后就是对于基于
非链表
的实现的顺序容器deque
、string
和vector
而言,如果我们对其中间进行修改,那么它的指针或者迭代器必然就会发生一些难以预料的变化。
vector<string>sVec;
list<string> sList;
sList.insert(sList.begin(),"Hello");
//头部插入一个World
sVec.insert(sVec.begin(),"World");
//尾部增加串“nihao"
sVec.push_back(