繁体   English   中英

C ++ std :: map和std :: set是否会删除副本值,从而使迭代器无效

[英]Does C++ std::map and std::set erase copy values and thus invalidate iterators

我正在考虑在二进制搜索树中实施删除。 我知道std :: map和std :: set通常实现为RBTree。 但是,不管它是不是BST,那么在擦除/删除有2个子节点的节点时,我们通常会与其后继者(或前任者)交换它。 许多教科书显示,这只是复制继任者的关键和价值。 但是此副本不是透明的。 例如,如果该值是我拥有出色迭代器的另一个容器,则删除一些OTHER键,现在值可以使我必须具有的迭代器对我没有明确更改的容器无效。 除了复制键值之外,我们还可以仅将后继节点重新链接到应删除该节点的树中。 这将保持节点的状态。

假设我有一张向量图:

std::map<int, vector<int> > mymap;
// Add root
mymap.insert(make_pair(2, vector<int>()));
// Add left child
mymap.insert(make_pair(1, vector<int>()));
// Add right child
mymap.insert(make_pair(3, vector<int>()));
mymap[3].push_back(10);
// I get an iterator to the vector stored in the node with key=3
vector<int>::iterator it = mymap[3].begin();

// I now remove element 2 from the map.  
mymap.erase(2);

// If the successor's (i.e. 3's) key,value were *copied* to 2's node
// and then 3's node was deleted this would invalid iterators 
vector<int>::iterator it2 = mymap[3].begin();

// These could/should be different if remove copied the successor
cout << &*it << " " << &*it2 << endl;

当然,包含3的节点可以在树中代替2的节点并删除2的节点而被“重新链接”。 这将保持键值的正确状态。

我对这个测试用例进行了编码,它似乎是重新链接而不是复制。 该行为在任何地方都有定义吗?

类似地,如果我有一个向量的向量(即向量>,并且我对一个“内部”向量中的元素持有一个迭代器,但是由于插入另一个向量而使外部向量改变了大小,那么即使我没有,迭代器也是无效的)从技术上修改了内部向量,无效的“传递”属性是在某个地方拼写出来的,还是“明显” /“暗示”的,我对游戏很慢?

基于节点的容器(映射,集合,列表)的擦除操作仅使迭代器和对已擦除元素的引用无效,而使其他任何迭代器无效。

当您按值擦除时,这通常是简单明了的,但是当您使用迭代器擦除时,在循环遍历整个容器的常见情况下确实需要注意。 以机智:

for (auto it = my_set.begin(); it != my_set.end(); )
{
     if (should_we_erase(it)) { my_set.erase(it++); }
     else                     { ++it;               }
}

请密切注意在擦除后如何确保使用迭代器! 在擦除分支中,我们首先通过后增量将it设置为一个新的有效迭代器,然后在原始迭代器中进行擦除(这是后增量表达式的值)。 或者,我们可以这样写: it = my_set.erase(it); ,因为erase操作会返回下一个(有效的)迭代器。 新手经常犯的一个错误是将++it放入循环头中,然后循环头在无效的迭代器上执行算术运算。

相比之下,vector和deque在许多操作中会使更多的迭代器和引用无效。

迭代器和引用的无效已作为标准库规范的一部分得到了很好的记录,并且任何自重的文档或教程都将使何时和如何使某些内容无效变得非常清楚。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM