向顺序容器添加元素
- push
- push_back
- push_front
- insert
- implace
向vector、string或deque插入元素有可能使所有指向容器的迭代器、引用和指针失效。因为添加元素时可能引起整个对象空间的重新分配(旧的空间不足以容纳插入新元素后的对象)。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移动到新的空间中。
作者说:
使用添加、删除等容器操作时,首先考虑容器分配元素空间的策略
push_back
向容器尾部添加元素
除array和forward_list外,每个顺序容器都支持。(不需要强记,考虑容器分配元素空间的策略)
container.push_back(word);
作者说:
当我们用一个对象来初始化容器,或将一个对象插入到容器中时,实际上放入的是对象值的一个拷贝,而不是对象本身。也不是对象的引用,两者之间没有任何关联。
push_front
将元素插入到容器头部
仅list、forward_list和deque支持(不需要强记,考虑容器分配元素空间的策略)
for(size_t ix = 0; ix != 4; ++ix )
{
ilist.push_front(ix);
}
每个元素都插入到容器的头部,最终形成逆序。
这一特性我们可以加以利用,比如重写一下Leetcode整数反序?
insert
在任意位置插入0个或多个元素
vector、deque、list和string都支持
forward_list有特殊版本的insert
insert有多个重载函数
- insert(itr, t) 在itr所指位置之前插入t
- insert(itr, n, t) 在itr所指位置之前插入n个t
- insert(itr, itr1, itr2) 在itr所指位置之前插入[itr1,itr2)范围内的元素
- insert(itr, il) 在itr所指位置之前插入一个{}列表
共同特点:
- 第一个参数都是迭代器,用来指定插入位置
- 都将元素插入到给定迭代器所指位置之前
- 返回一个迭代器,指向新添加的第一个元素
insert函数也具备push_back和push_front的功能
list.insert(list.begin(), 1);
list.insert(list.end(), 1);
在首元素之前插入,即是push_front
在尾后元素之前插入,即是push_back
利用insert函数的返回值
list<string> lst;
auto iter = lst.begin();
while( cin >> word )
{
iter = lst.insert( iter, word );
}
等价于一直在调用push_front
emplace
新标准引入
push和insert插入的都是现成的对象
emplace则是创建一个对象插入到容器中
因此,它的参数与容器元素类型的构造函数的参数一致
struct Sales_data{
Sales_data(const string &s, unsigned n, double p);
};
list<Sales_data> c;
c.emplace("934", 25, 1.2);
使用emplace只需要记住这一区别就可以了,其他可以作类比
push_front----emplace_front
push_back----implace_back
insert-----implace
访问元素
- back
- front
- 下标
- at
返回值是对应位置元素的引用
想要得到容器首尾位置的元素,可以
auto val1 = *c.begin();
auto last = c.end();
auto val2 = *(--last);
更为简单的方法是使用front和back
auto val1 = c.front();
auto val2 = c.back();
注意使用之前需要判断容器是否为空
at和下标相比,如果出现下标越界的情况,at会抛出一个out_of_range异常
删除元素
- pop
- pop_back
- pop_front
- erase
- clear
可以和插入的操作对比来理解
- erase(itr)
- erase(itr1, itr2)
erase的返回值也是一个迭代器,指向最后一个被删除元素之后
一个典型的例子,删除List中的所有奇数元素
list<int> lst = {0,1,2,3,4,5,6,7,8,9};
auto it = lst.begin();
while( it != lst.end() )
{
if( *it % 2 )
{
it = lst.erase(it);
}
else
{
++it;
}
}
forward_list操作
前面提到,首先考虑容器分配元素空间的策略。forward_list正是因为分配策略的不同,需要特殊的操作来支持
ele1-->ele2-->ele3-->ele4
ele1-->ele2--------->ele4
每个元素除了保存元素值之外,还要保存后一个元素的地址。
因此删除ele3,需要修改ele2的后继,而我们又没有办法通过ele3拿到ele2。
forward_list中添加或删除元素的操作是通过改变给定元素之后的元素来完成的。
insert_after(p,t)
insert_after(p,n,t)
insert_after(p,b,e)
insert_after(p, il)
erase_after(p)
erase_after(b,e)
implace_after(p,args)
同样的道理,尾后迭代器对forward_list来说也是没有意义的,因此增加了首前迭代器
before_begin()
改变容器大小
c.resize(n, t)
如果缩小容器,则容器后部的元素会被删除
如果放大容器,则使用t来初始化新添加的元素
t是可选的
迭代器失效
前面也提到了,添加、删除元素可能导致迭代器失效
建议在改变容器的操作后面,都重新定位迭代器
vector<int> vi = {0,1,2,3,4,5,6,7,8,9};
auto itr = vi.begin();
while( itr != vi.end() )
{
if( *itr % 2 )
{
itr = vi.insert( itr, *itr );
itr += 2;
}
else
{
itr = vi.erase( itr );
}
}
这里使用了insert和erase的返回值来重新定位迭代器
而且循环条件中,使用了vi.end(),而不是提前获取
auto end = vi.end();
wihle( begin != end ){}
增删操作后,end同样会发生改变,因此应当即时获取
- 使用返回值重新定位迭代器
- 即时获取end,不要缓存end值