对象是否可以从标准C++容器中擦除自身

Can an object erase itself from a standard C++ container?

本文关键字:擦除 C++ 是否 标准 对象      更新时间:2023-10-16

以下代码

#include <iostream>
#include <map>
struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
  }
};
int main()
{
  std::map<int, foo> m;
  m.emplace(1, foo() );
  std::cout << m.size() << std::endl;
  m[1].kill(m, 1);
  std::cout << m.size() << std::endl;
}

编译而不发出警告 (G++(,执行时没有错误,并且从输出判断,kill 方法会从映射中删除foo对象。但是,我觉得这实际上可能是未定义的行为。似乎在kill方法中,行之后的m.erase(i) this不再指向有效对象。

C++标准对此有何规定?

当你输入你的kill时,m[1](从m[1].kill(m, 1);(语句已经被完全评估为你正在调用killfoo对象。

然后你m.erase(i);最终破坏当前对象foo

只要你在从kill函数返回之前绝对不使用当前对象(this(编写任何语句,这是完全可以接受和安全的(正如Auriga和Barry引用的帖子所评论的那样(。即使当前对象不再存在,您的函数也会从堆栈中安全返回,据我所知,它没有理由失败。

作为例证,这将最终导致未定义的行为,并且不得这样做:

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
    cout << attribute; // don't do that! current foo object does not exist anymore
  }
  int attribute;
};

因此,假设您正在做的事情是有风险的,但如果您做得好,则有效且安全。

作为说明,这将最终定义行为并且可以完成:

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    int theAttribute = attribute;
    m.erase(i);
    cout << theAttribute; // OK!
  }
  int attribute;
};

无论如何,让方法删除当前对象可能不是一个好的做法(特别是如果另一个开发人员稍后修改代码......他可以很容易地用上面的第一个例子让它崩溃(。至少在代码中放置一个明确的注释,以告诉当前对象可能已被销毁(请注意,kill可以销毁当前对象、另一个对象或没有......取决于m内容和i(:

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
    // careful! current object could have been destroyed by above statement and may not be valid anymore! Don't use it anymore!
  }
};

这根本不安全。 如果多次调用,m.erase(i)将避免尝试擦除同一对象,但如果多次调用m[1].kill(m, 1)则为未定义的行为。为了稍微安全一点,m.at(1).kill(m, 1)会抛出out_of_range错误。