简体   繁体   English

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

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

I'm considering the implementation of removal in binary search tree. 我正在考虑在二进制搜索树中实施删除。 I know that std::map and std::set are usually implemented as an RBTree. 我知道std :: map和std :: set通常实现为RBTree。 But regardless, if it is some BST then when erasing/removing a node that has 2 children we generally swap it with its successor (or predecessor). 但是,不管它是不是BST,那么在擦除/删除有2个子节点的节点时,我们通常会与其后继者(或前任者)交换它。 Many textbooks show this as just copying over the successor's key and value. 许多教科书显示,这只是复制继任者的关键和价值。 But this copy is not transparent. 但是此副本不是透明的。 For example, if the value is another container to which I have outstanding iterators, then removing some OTHER key,value could now invalidate the iterators I have to a container that I have not explicitly changed. 例如,如果该值是我拥有出色迭代器的另一个容器,则删除一些OTHER键,现在值可以使我必须具有的迭代器对我没有明确更改的容器无效。 Rather than copying key,value we could just relink the successor node into the tree where the node is that should be deleted. 除了复制键值之外,我们还可以仅将后继节点重新链接到应删除该节点的树中。 This would maintain the state of the node. 这将保持节点的状态。

Suppose I have a map of vectors: 假设我有一张向量图:

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;

Certainly the node containing 3 could just be "relinked" within the tree in place of 2's node and 2's node deleted. 当然,包含3的节点可以在树中代替2的节点并删除2的节点而被“重新链接”。 This would maintain the correct state of the key,value. 这将保持键值的正确状态。

I coded up this test case and it appeared to relink rather than copy. 我对这个测试用例进行了编码,它似乎是重新链接而不是复制。 Is that behavior defined anywhere? 该行为在任何地方都有定义吗?

Similarly if I have a vector of vectors (ie vector > and I hold an iterator to an element in one of the "inner" vectors but then the outer vector resizes due to insertion of another vector then my iterator is invalid even though I haven't technically modified that inner vector. Is that "transitive" property of invalidation spelled out somewhere or is it "obvious"/"implied" and I'm just slow to the game? 类似地,如果我有一个向量的向量(即向量>,并且我对一个“内部”向量中的元素持有一个迭代器,但是由于插入另一个向量而使外部向量改变了大小,那么即使我没有,迭代器也是无效的)从技术上修改了内部向量,无效的“传递”属性是在某个地方拼写出来的,还是“明显” /“暗示”的,我对游戏很慢?

The erase operations of the node-based containers (maps, sets, lists) invalidate only iterators and references to the element that was erased, but not any other iterators. 基于节点的容器(映射,集合,列表)的擦除操作仅使迭代器和对已擦除元素的引用无效,而使其他任何迭代器无效。

When you erase by value, this is usually straight-forward, but when you erase by iterator, this does require care in the common case of iterating over the entire container in a loop. 当您按值擦除时,这通常是简单明了的,但是当您使用迭代器擦除时,在循环遍历整个容器的常见情况下确实需要注意。 To wit: 以机智:

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

Pay close attention how we make sure to not use the iterator after we erased it! 请密切注意在擦除后如何确保使用迭代器! In the erasing branch, we first set it to a new valid iterator one further via the post-increment, and then erase at the original iterator (which is the value of the post-increment expression). 在擦除分支中,我们首先通过后增量将it设置为一个新的有效迭代器,然后在原始迭代器中进行擦除(这是后增量表达式的值)。 Alternatively, we can write this as it = my_set.erase(it); 或者,我们可以这样写: it = my_set.erase(it); , since the erase operation returns the next (valid) iterator. ,因为erase操作会返回下一个(有效的)迭代器。 A very common novice mistake is to put ++it int he loop header, which then ends up performing arithmetic on the invalid iterator. 新手经常犯的一个错误是将++it放入循环头中,然后循环头在无效的迭代器上执行算术运算。

By contrast, vector and deque invalidate a lot more iterators and references during a lot more operations. 相比之下,vector和deque在许多操作中会使更多的迭代器和引用无效。

The invalidation of iterators and references is well documented as part of the standard library specification, and any self-respecting documentation or tutorial will make it very clear when and how something is invalidated. 迭代器和引用的无效已作为标准库规范的一部分得到了很好的记录,并且任何自重的文档或教程都将使何时和如何使某些内容无效变得非常清楚。

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

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