STL 注意事项
2017-02-08 10:03
232 查看
STL中的容器的存储分为顺序存储(如vector、deque)与链式存储(如list、map、set)。
对于map与list这样的链式存储结构。我们一般可以有两种方法
1. 使用erase(iter++),因为iter2 = iter++是iter先移到指向下一个节点,而iter2还是指向当前的节点。
for(;iter!=mapStudent.end();)
{
if((iter->second)>=aa)
{
//满足删除条件,删除当前结点,并指向下面一个结点
mapStudent.erase(iter++);
}
else
{
//条件不满足,指向下面一个结点
iter++;
}
}
这种删除方式也是STL源码一书中推荐的方式,分析 mapStudent.erase(iter++)语句,map中在删除iter的时候,先将iter做缓存,然后执行iter++使之指向下一个结点,再进入erase函数体中执行删除操作,删除时使用的iter就是缓存下来的iter(也就是当前iter(做了加操作之后的iter)所指向结点的上一个结点)。
根据以上分析,可以看出mapStudent.erase(iter++)和map
Student.erase(iter); iter++;这个执行序列是不相同的。前者在erase执行前进行了加操作,在iter被删除(失效)前进行了加操作,是安全的;后者是在erase执行后才进行加操作,而此时iter已经被删除(当前的迭代器已经失效了),对一个已经失效的迭代器进行加操作,行为是不可预期的,这种写法势必会导致 map操作的失败并引起进程的异常。
2. erase的返回值会指向下一个节点
for(TStrMapIter
iter= strmap.begin(); iter!= strmap.end();)
{
if
("somevalue" == iter->second )
{
iter
= strmap.erase(iter);
}
else
{
++iter;
}
}
但对于顺序存储第一种用法却是错误的。因为顺序存储的容器一旦erase时,会涉及到数据移动,iterator所指的位置还是那个位置,但元素却移动了,iter++之后已不再你想要的元素位置了。iter = strmap.erase(iter)与iter++对于顺序容器有何不同?
源码剖析
以STL list为例:
iterator的相关操作
_Self& operator++()
{
this->_M_incr();
return
*this;
}
_Self operator++(int)
{ _Self
__tmp = *this;
this->_M_incr();
return
__tmp; //后缀++按照语意返回了++前的iterator,
}
void _M_incr() { _M_node = _M_node->_M_next; } //++的操作对于list结构来说,就是使iterator的_M_node指向下一个结点
iterator erase(iterator __position)
{ _List_node_base*
__next_node = __position._M_node->_M_next;
_List_node_base*
__prev_node = __position._M_node->_M_prev;
_Node*
__n = (_Node*) __position._M_node;
__prev_node->_M_next
= __next_node;
__next_node->_M_prev
= __prev_node; //上面的代码把删除结点__position的前后结点串起来,而移除_positoin
_STLP_STD::_Destroy(&__n->_M_data);
//call T::~T()
this->_M_node.deallocate(__n,
1); //释放结点内存
return
iterator((_Node*)__next_node);
}
分析代码我们可以看出,erase会deallocate__position的_M_node, 在__position上再进行++是错误的。
所以不能在m_list.erase(iter)后,进行iter++.
哪为什么m_list.erase(iter++)可以呢?为什么不能用m_list.erase(++iter)?
参照operator++的代码我们可以找到答案,iter++返回了++之前的iter值,erase使用这个值能正确进行__position的前后结点的串接及删除正确的结点,而++iter返回的是++之后的iter,所以m_list.erase(++iter)串接不正确,iter->_M_node也是失效的.
对于非结点类,如数组类的容器vector,string,deque,如果erase会返回下个有效的iterator,可以这样处理:
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if(需要删除)
{
iter=m_vector.erase(iter);
}
else
++iter;
}
对于map与list这样的链式存储结构。我们一般可以有两种方法
1. 使用erase(iter++),因为iter2 = iter++是iter先移到指向下一个节点,而iter2还是指向当前的节点。
for(;iter!=mapStudent.end();)
{
if((iter->second)>=aa)
{
//满足删除条件,删除当前结点,并指向下面一个结点
mapStudent.erase(iter++);
}
else
{
//条件不满足,指向下面一个结点
iter++;
}
}
这种删除方式也是STL源码一书中推荐的方式,分析 mapStudent.erase(iter++)语句,map中在删除iter的时候,先将iter做缓存,然后执行iter++使之指向下一个结点,再进入erase函数体中执行删除操作,删除时使用的iter就是缓存下来的iter(也就是当前iter(做了加操作之后的iter)所指向结点的上一个结点)。
根据以上分析,可以看出mapStudent.erase(iter++)和map
Student.erase(iter); iter++;这个执行序列是不相同的。前者在erase执行前进行了加操作,在iter被删除(失效)前进行了加操作,是安全的;后者是在erase执行后才进行加操作,而此时iter已经被删除(当前的迭代器已经失效了),对一个已经失效的迭代器进行加操作,行为是不可预期的,这种写法势必会导致 map操作的失败并引起进程的异常。
2. erase的返回值会指向下一个节点
for(TStrMapIter
iter= strmap.begin(); iter!= strmap.end();)
{
if
("somevalue" == iter->second )
{
iter
= strmap.erase(iter);
}
else
{
++iter;
}
}
但对于顺序存储第一种用法却是错误的。因为顺序存储的容器一旦erase时,会涉及到数据移动,iterator所指的位置还是那个位置,但元素却移动了,iter++之后已不再你想要的元素位置了。iter = strmap.erase(iter)与iter++对于顺序容器有何不同?
源码剖析
以STL list为例:
iterator的相关操作
_Self& operator++()
{
this->_M_incr();
return
*this;
}
_Self operator++(int)
{ _Self
__tmp = *this;
this->_M_incr();
return
__tmp; //后缀++按照语意返回了++前的iterator,
}
void _M_incr() { _M_node = _M_node->_M_next; } //++的操作对于list结构来说,就是使iterator的_M_node指向下一个结点
iterator erase(iterator __position)
{ _List_node_base*
__next_node = __position._M_node->_M_next;
_List_node_base*
__prev_node = __position._M_node->_M_prev;
_Node*
__n = (_Node*) __position._M_node;
__prev_node->_M_next
= __next_node;
__next_node->_M_prev
= __prev_node; //上面的代码把删除结点__position的前后结点串起来,而移除_positoin
_STLP_STD::_Destroy(&__n->_M_data);
//call T::~T()
this->_M_node.deallocate(__n,
1); //释放结点内存
return
iterator((_Node*)__next_node);
}
分析代码我们可以看出,erase会deallocate__position的_M_node, 在__position上再进行++是错误的。
所以不能在m_list.erase(iter)后,进行iter++.
哪为什么m_list.erase(iter++)可以呢?为什么不能用m_list.erase(++iter)?
参照operator++的代码我们可以找到答案,iter++返回了++之前的iter值,erase使用这个值能正确进行__position的前后结点的串接及删除正确的结点,而++iter返回的是++之后的iter,所以m_list.erase(++iter)串接不正确,iter->_M_node也是失效的.
对于非结点类,如数组类的容器vector,string,deque,如果erase会返回下个有效的iterator,可以这样处理:
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if(需要删除)
{
iter=m_vector.erase(iter);
}
else
++iter;
}
相关文章推荐
- 01-ZYNQ学习之认识 Xilinx ZYNQ
- flatMap处理嵌套List flatMap嵌套map
- Python实现定时任务
- sql语句根据月份动态的求平均数
- mysql与ftp连接过慢的原因(一半因为域名解析导致)
- 使用Ambari快速部署Hadoop大数据环境
- 单链表的排序
- C# 6 与 .NET Core 1.0 高级编程 - 37 章 ADO.NET
- spring事务中read-only配置
- Android N 版本迭代/应用内升级 FileUriExposedException问题处理
- 博士这五年-李沐
- 接口测试从零开始系列_mock技术使用
- Android中两个Activity之间数据传递及返回
- glibc的几个有用的处理二进制位的内置函数
- SparkR的R3.3.1源码安装
- Google Analytics与Google Adwords的转换数据差别
- 安卓开发异常之——java.lang.NoClassDefFoundError: com.umeng.analytics.d的问题
- 倾囊相授DDoS防护十二式
- c++ unsigned和int类型转换及运算
- 【译】人们购买的是更好的自己