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

Bob*_*bby 11 c++ containers undefined-behavior

以下代码

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

C++标准对此有何看法?

jpo*_*o38 7

当您输入时kill,m[1](from m[1].kill(m, 1);)语句已被完全评估为foo您正在调用的对象kill.

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

至于this在从kill函数返回之前绝对没有使用当前对象()编写语句,这是完全可以接受和安全的(由AurigaBarry引用的帖子评论).即使当前对象不再存在,您的函数也会从堆栈中安全返回,据我所知,它没有理由失败.

作为一个例子,这将导致未定义的行为,并且不能完成:

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;
};
Run Code Online (Sandbox Code Playgroud)

所以,让我们说你正在做的事情是有风险的,但如果你做得好,那就是有效和安全的.

作为一个例子,这将最终定义行为并且可以完成:

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

有一个方法删除当前对象可能不是一个好的做法(特别是如果另一个开发人员稍后修改代码......他可以很容易地使它崩溃与上面的第一个例子).至少在代码中放置一个明确的注释来告诉当前对象可能已经被销毁(注意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!
  }
};
Run Code Online (Sandbox Code Playgroud)