【问题标题】:Order of destruction in unordered_mapunordered_map 中的破坏顺序
【发布时间】:2015-09-30 08:59:55
【问题描述】:

我有一个 unordered_map 对象。每个对象在其析构函数中浏览无序映射以查找其他对象,然后调整这些其他对象。如果其他对象是僵尸对象,这将失败,但如果其他对象完全从 unordered_map 中删除,则没有问题。

我的问题:

  1. 如果我擦除()一个对象,并且它的析构函数试图在无序映射中寻找自己,这是否有效?具体来说,是先调用析构函数,还是先从unordered_map中取出对象,还是没有保证?
  2. 如果 unordered_map 被破坏,这会起作用吗?具体来说,当调用每个单独的析构函数时,unordered_map 是否处于有效状态?

【问题讨论】:

  • 用一些打印语句来测试一下?
  • erase 只会使其自己的迭代器无效,但我认为您不能假设一旦调用析构函数就可以在地图中找到自己。也不要假设如果您测试并且它适用于一种实现,它将适用于标准库的其他实现。正如@KerrekSB 所说 - 不要这样做

标签: c++ standard-library


【解决方案1】:

类型 T 的对象的生命周期结束时 [...] 如果 T 是具有非平凡析构函数 (12.4) 的类类型,则析构函数调用开始 [...]

[§ 3.8/1 N4431]

而且,再往下

本国际标准中赋予对象的属性仅在其生命周期内适用于给定对象

[§ 3.8/3 N4431]

最后

[...] 在对象的生命周期结束后 [...] 任何指向对象将要或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。 [...] 如果 [...] 指针用于访问非静态数据成员或调用对象的非静态成员函数 [...]

,则程序具有未定义的行为

[§ 3.8/5 N4431]

因此,由于您必须对映射有某种引用(例如,指针或真正的引用,我在这里也将其视为指针),并且它的生命周期已经结束,访问成员函数(到例如,获取一个迭代器)将 - 据我阅读标准的这一部分 - 导致未定义的行为。

我也在看标准中关于无序容器和一般容器的部分,找不到上述异常或关于销毁期间状态的任何线索。

所以:不要这样做。既不用于无序容器,也不用于任何其他对象。


顺便说一句:当您对随后将被破坏的对象进行调整时,什么样的调整才有意义?

【讨论】:

  • 谢谢,标准是我所希望的。当一个即将被杀死的对象的析构函数有副作用时(例如,如果它进行垃圾回收),调整即将被杀死的对象很重要。
【解决方案2】:

我想我找到了一个不错的解决方案。您可以将 unordered_map 封装在一个类中,并使用它的析构函数来引发标志并将其检测为哈希表中 typaram 对象的析构函数中的边缘情况。像这样:

template<typename K, typename V>
struct hash_table
{
    unordered_map<K, V> map;
    bool is_being_deleted = false;
    ~hash_table()
    {
        is_being_deleted = true;
    }
};

struct PageRefrence
{
    string str;
    int page;
    hash_table<string, PageRefrence>& refTable;
    ~PageRefrence()
    {
        if (refTable.is_being_deleted == true) // When the map is in the procces of deletion
            return;
        else
        { // Normal case
            auto x = refTable.map.find(str);
            cout << (*x).second.page;
        }

    }
};

int main()
{
    hash_table<string, PageRefrence> refTable;
    refTable.map.insert({ "HELP",{"HELP",42,refTable} });
}

【讨论】:

  • 我认为您需要交换声明。销毁以相反的顺序进行。你需要让is_being_deleted map 存活的时间更长。优化器可能会杀死您版本中的 is_being_deleted = true 写入,因为它是对子对象的写入,该子对象的生命周期在写入后立即结束。
猜你喜欢
  • 2016-12-28
  • 2011-03-31
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 2011-09-04
  • 1970-01-01
  • 2016-01-18
  • 2016-08-27
相关资源
最近更新 更多