【问题标题】:Validity of std::map::iterator after erasing elements擦除元素后 std::map::iterator 的有效性
【发布时间】:2013-09-12 23:17:34
【问题描述】:

我编写了一个代码来解决以下问题:我们有一个map<double,double>,其中包含(相对)大量的项目。我们希望合并相邻的项目以减小地图的大小,同时保持一定的“损失因子”尽可能低。

为此,我首先填充一个包含相邻迭代器和相关损失因子的列表(假设每个列表元素具有以下类型:

struct myPair {
    map<double,double>::iterator curr, next;
    double loss;
    myPair(map<double,double>::iterator c, map<double,double>::iterator n, 
        double l):  curr(c), next(n), loss(l) {}
};

)。这样做如下:

for (map<double,double>::iterator it1 = myMap.begin(); it1 != --(myMap.end()); 
    it1++) {
    map<double,double>::iterator it2 = it1; it2++;
    double l = computeLoss(it1,it2); 
    List.push(myPair(it1,it2,l));
}

然后,我从mapinsert中找到与最低损失因子对应的列表元素erase,在@中找到一个新元素(合并currnext的结果) 987654329@。由于这也会更改与next 之后或curr 之前的元素对应的列表元素,因此我更新了相应的条目以及相关的损失因子。

(我没有详细介绍如何有效地实现上述内容,但基本上我将双链表和堆结合起来)。

虽然erase 操作不应使程序的某些特定输入实例的剩余迭代器无效,但我在尝试从map 中删除元素时得到了double free or corruption 错误。

我试图跟踪这一点,似乎当两个地图元素的firstsecond 条目非常接近时(更准确地说,当currnextfirsts 非常接近时,就会发生这种情况关闭)。

奇怪的是,我在填充列表时放置了一个assert,以确保在删除元素的循环中所有条目中的currnext 是不同的和相同的assert。第二个失败了!

如果有人可以帮助我,我将不胜感激。

附:很抱歉我说的不是很精确,但我想尽量减少细节。

更新:这是(一个非常简化的版本)我如何从地图中删除元素:

while (myMap.size() > MAX_SIZE) {
    t = list.getMin();
    /* compute the merged version ... let's call the result as (a,b) */
    myMap.erase(t.curr);
    myMap.erase(t.next);
    myMap.insert(pair<double,double>(a,b));

    /* update the adjacent entries */
}

【问题讨论】:

  • 请显示删除元素的代码。
  • 我现在就添加代码。
  • 顺便说一下,在所示示例中,a &gt;= t.curr-&gt;firsta &lt;= t.next-&gt;first,因此新元素将被替换在与两个已删除元素相同的位置。
  • 看起来你想要类似于堆或树的东西而不是列表,这样你就不必搜索最小的损失因子。在这种情况下,更新成本 ln(N)。
  • 旁注:使用double 作为映射键可能会产生相当令人惊讶的结果。如果您通过计算获得键值,您可能会在期望不精确的地方获得不同的键,这仅仅是由于精度错误。

标签: c++ dictionary iterator std stdmap


【解决方案1】:

正如其他人已经提到的那样,使用double 作为map 的键是有问题的。特别是在计算值时。

因此,我的解决方案是使用std::multimap 而不是map(然后在填充地图后使用相同的键合并元素)。有了这个,例如即使a 非常接近t.currt.next 或任何其他元素的两个键,insert 操作肯定会创建一个新元素,使得@ 中没有现有的iterator 987654330@ 会指出这一点。

【讨论】:

    【解决方案2】:

    myPair 中存储的迭代器在容器修改后仍然无效。你应该避免这种技术。可能当您查看头文件时,您会发现一些现成的草稿用于您的任务?

    【讨论】:

    • 你确定吗?因为例如在cplusplus.com/reference/map/map/erase 中提到了已擦除的迭代器无效但其余的迭代器仍然有效(在上面的代码中,t 已从list 中删除)。
    • 是的,你是对的。映射迭代器在容器修改后保持有效。但是当我们在下一次迭代中擦除 myMap.erase(t.next) 之后会发生什么情况? /跨度>
    • 你是绝对正确的。我省略了大部分代码以避免复杂化。但实际上存储在list 中的迭代器在erase 操作之后更新。特别是,条目中指向t.curr(现在无效)的next 元素将被新插入的元素替换(map::insert 将返回一个指向该元素的迭代器)。同样,指向 t.next 的条目的 curr 元素将被指向新插入元素的迭代器替换。
    猜你喜欢
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 2011-10-24
    • 2012-11-23
    • 1970-01-01
    相关资源
    最近更新 更多