迭代器的失效问题:对容器的操作影响了元素的存放位置,称为迭代器失效。迭代器失效的情况:
当容器调用erase()
方法后,当前位置到容器末尾元素的所有迭代器全部失效。
当容器调用insert()
方法后,当前位置到容器末尾元素的所有迭代器全部失效。
如果容器扩容,在其他地方重新又开辟了一块内存。原来容器底层的内存上所保存的迭代器全都失效。
迭代器失效
迭代器失效有三种情况,由于底层的存储数据结构,分三种情况
序列式迭代器失效。
序列式容器(std::vector
和std::deque
),其对应的数据结构分配在连续的内存中,对其中的迭代器进行insert
和erase
操作都会使得删除点和插入点之后的元素挪位置,进而导致插入点和删除掉之后的迭代器全部失效。可以利用erase
迭代器接口返回的是下一个有效的迭代器。
链表性迭代器失效。
链表型数据结构(std::list
)使用链表进行数据存储,插入或者删除只会对当前的节点造成影响,不会影响其他的迭代器。可以利用erase
迭代器接口返回的是下一个有效的迭代器,或者将当前的迭代器指向下一个erase(iter++)
。
树形迭代器失效。
树形数据结构,如map
, set
,multimap
,multiset
等,使用红黑树进行数据存储,删除当前的迭代器,仅仅会使当前的迭代器失效。erase
迭代器的返回值为 void,可以采用erase(iter++)
的方式进行删除。
在实现上有两种模板,其一是通过 erase
获得下一个有效的 iterator
,使用于序列式迭代器和链表式迭代器
//
for (iter = cont.begin(); iter != cont.end();) {
(*it)->doSomething();
if (shouldDelete(*iter))
iter = cont.erase(iter); //erase删除元素,返回下一个迭代器
else
++iter;
}
复制代码
其二是,递增当前迭代器,适用于链表式迭代器和树形迭代器。
// 递增当前迭代器
for (iter = cont.begin(); it != cont.end();) {
(*iter)->doSomething();
if (shouldDelete(*iter))
cont.erase(iter++);
else
iter++;
}
复制代码
下面按照分类对迭代器失效进行一些代码示例的展示。
序列迭代器
对于std::vector
和std::deque
,删除迭代器会导致删除点之后的所有迭代器失效。可以利用erase
接口返回的是下一个有效的迭代器的信息进行处理。
/**
* @brief: 序列迭代器失效处理
* 将erase返回的下一个有效的迭代器赋值给iter,来避免迭代器失效
*
* 操作示例:将数据是偶数的迭代器删除
*/
void vector_iter_erase() {
std::vector<int> vect {0, 1, 2, 3, 4, 5};
for (auto iter = vect.begin(); iter != vect.end();) {
if (*iter & 1) {
//
iter = vect.erase(iter);
} else {
iter++;
}
}
// 展示所有的有效数据
std::cout << "The vector item's ";
for (auto iter = vect.begin(); iter != vect.end(); iter++) {
std::cout << *iter << " ";
}
std::cout << std::endl;
}
复制代码
链表迭代器
链表型数据结构(std::list
)插入或者删除只会对当前的节点造成影响,不会影响其他的迭代器。可以利用erase
迭代器接口返回的是下一个有效的迭代器,或者递增迭代器erase(iter++)
。
/**
* @brief: 链表迭代器失效处理,有两种方式:
* 1. 将erase返回的下一个有效的迭代器赋值给iter,来避免迭代器失效
* 2. 将当前的迭代器iter++,指向下一个有效迭代器
*
* 操作示例:将数据是偶数的迭代器删除
*/
void list_iter_erase() {
std::list<int> lst {0, 1, 2, 3, 4, 5};
#if 0
for (auto iter = lst.begin(); iter != lst.end();) {
if (*iter & 1) {
lst.erase(iter++);
} else {
iter++;
}
}
#else
for (auto iter = lst.begin(); iter != lst.end();) {
if (*iter & 1) {
iter = lst.erase(iter);
} else {
iter++;
}
}
#endif
// 展示所有有效数据
std::cout << "The list item's ";
for (auto iter = lst.begin(); iter != lst.end(); iter++) {
std::cout << *iter << " ";
}
std::cout << std::endl;
}
复制代码
树形迭代器
树形数据结构,如map
, set
,multimap
,multiset
等,删除当前的迭代器,仅仅会使当前的迭代器失效。erase
迭代器的返回值为 void,可以采用erase(iter++)
的方式进行删除。
/**
* @brief: 树形迭代器失效处理,erase接口返回值时void
* 指向下一个有效迭代器来避免迭代器失效
*
* 操作示例:将数据是key是奇数的迭代器删除
*/
void map_iter_erase() {
std::unordered_map<int, int> umap {
{0, 1}, {1, 6}, {2, 6}, {3, 6}, {4, 8}, {5, 8}
};
for (auto iter = umap.begin(); iter != umap.end();) {
if (iter->first & 1) {
umap.erase(iter++);
} else {
iter++;
}
}
std::cout << "The unordered_map item's " << std::endl;
for (auto iter = umap.begin(); iter != umap.end(); iter++) {
std::cout << "\t" << iter->first << " : " << iter->second << std::endl;
}
std::cout << std::endl;
}
复制代码
参考资料
【C++ STL】迭代器失效的几种情况总结
评论