【问题标题】:Is map::find safe?map::find 安全吗?
【发布时间】:2020-02-02 11:59:50
【问题描述】:

我正在尝试测试“find”在 std:map 中是否安全,所以我在使用“find”测试后删除了一个元素,但该元素的迭代器仍然有效。即使我再次使用 find ,它也会再次找到擦除的元素。

根据文档:

引用被函数删除的元素的迭代器、指针和引用无效。 所有其他迭代器、指针和引用保持其有效性。

  • 为什么第二个迭代器auto it_2 = numeros.find("uno");如果元素被擦除了还能找到它?

  • 为什么std::cout << it->first << " : " << it->second << std::endl; 在“擦除”后打印元素?这意味着从地图中删除元素时 find 是安全的?

这是我的例子。

#include <iostream>
#include <map>

int main(int argc, char *argv[])
{
    std::map<std::string,unsigned int> numeros = { {"uno",1}, {"dos",2}, {"tres",3}};

    auto it = numeros.find("uno");
    std::cout << it->first << " : " << it->second << std::endl;

    std::cout << std::endl;

    numeros.erase("uno");

    std::cout << it->first << " : " << it->second << std::endl;

    std::cout << std::endl;

    auto it_2 = numeros.find("uno");
    std::cout << it_2->first << " : " << it_2->second << std::endl;

    std::cout << std::endl;

    for (auto i=numeros.begin(); i!=numeros.end(); ++i)
        std::cout << i->first << " : " << i->second << std::endl;

    return 0;
}

输出

uno : 1

uno : 1

uno : 1

dos : 2
tres : 3

谢谢!

【问题讨论】:

  • erase 使所有当前映射迭代器无效,如果在此之后取消引用一个,则调用未定义的行为。 it_2 也将是一个结束迭代器,再次取消引用它将调用 ub 但您可以在取消引用之前对其进行测试以查看它是否为 numeros.end()
  • 在我的编译器中 it_2 不打印 uno : 1,它崩溃了。您应该在使用前检查它。
  • erase 指向它的元素后取消引用 it 获得正确的结果并不能保证您正在观察定义的行为。确实,正如@George 指出的那样,您正在观察未定义的行为。
  • @masoud 我使用 clang++ Apple clang 版本 11.0.0 它打印这个输出,但是正如 walnut 所说,我的程序有未定义的行为,在其他编译器下它可以做任何事情。

标签: c++


【解决方案1】:

numeros.erase("uno"); 根据您的报价使迭代器 it 无效。这意味着您不再被允许取消引用迭代器。无论如何,这样做有undefined behavior

因此,您的程序具有未定义的行为,因为您在下一行取消引用迭代器

std::cout << it->first << " : " << it->second << std::endl;

未定义的行为意味着您的程序可以做任何事情。不能保证任何特定的事情都会再发生。也不保证您会收到任何错误或警告。


(假设您纠正了上面未定义的行为:)

第二个find

auto it_2 = numeros.find("uno");

没有找到被擦除的元素。如果.find 没有找到任何元素,它会返回过去的迭代器numeros.end(),这就是这里发生的事情。取消引用过去的迭代器也有未定义的行为。所以下面一行

std::cout << it_2->first << " : " << it_2->second << std::endl;

取消对过去的迭代器的引用也会导致未定义的行为,并且您的程序没有行为保证。

您需要始终检查 findend 的结果,以验证它是否找到了一个元素:

if(it_2 != numeros.end()) {
    std::cout << it_2->first << " : " << it_2->second << std::endl;
} else {
    std::cout << "uno not found!" << std::endl;
}

【讨论】:

  • 是的,就是这样!我已经用.end() => if(it_2 == numeros.end()) { std::cout &lt;&lt; "erased" &lt;&lt; std::endl; } 进行了检查,它打印出“已擦除”。然后正如你所说,这两种情况都有未定义的行为。我的编译器打印了我之前显示的输出,但在其他情况下或在其他编译器下它可以做任何事情。 (我用的是clang++苹果clang版本11.0.0)
猜你喜欢
  • 2022-01-22
  • 2020-08-24
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多