【问题标题】:Does C++ std::map and std::set erase copy values and thus invalidate iteratorsC++ std::map 和 std::set 是否擦除复制值并因此使迭代器无效
【发布时间】:2017-08-06 23:14:04
【问题描述】:

我正在考虑在二叉搜索树中实现删除。我知道 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 的节点。这将保持键值的正确状态。

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

类似地,如果我有一个向量向量(即向量 > 并且我持有一个指向“内部”向量之一中的元素的迭代器,但是外部向量由于插入另一个向量而调整大小,那么即使我的迭代器无效我没有从技术上修改那个内部向量。无效的“传递”属性是在某处拼写出来的,还是“明显的”/“暗示的”,我只是玩得很​​慢?

【问题讨论】:

    标签: c++ dictionary iterator binary-search-tree


    【解决方案1】:

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

    当您按值擦除时,这通常是直截了当的,但是当您按迭代器擦除时,在循环中遍历整个容器的常见情况下确实需要小心。也就是说:

    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 在更多操作期间使更多迭代器和引用失效。

    作为标准库规范的一部分,迭代器和引用的失效已被详细记录,任何自尊的文档或教程都会非常清楚地说明何时以及如何失效。

    【讨论】:

      猜你喜欢
      • 2021-05-08
      • 2011-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-09-23
      • 2012-07-31
      • 1970-01-01
      • 2011-05-06
      • 2012-06-10
      相关资源
      最近更新 更多