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

时间:2016-05-01 21:23:07

标签: 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;
}

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

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

2 个答案:

答案 0 :(得分:7)

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

然后你m.erase(i);结束销毁当前对象foo

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

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

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内容和{ {1}}):

i

答案 1 :(得分:-4)

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