以下代码
#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 ++标准对此有何看法?
答案 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
错误。