所以这是我正在处理的代码:
class A
{
public:
A(){}
virtual ~A(){}
void Log(){printf("Log A\n");}
};
int main(int argc, char**argv)
{
A* a = new A();
a->Log(); // "Log A"
map<int,A*> m;
m[1] = a;
m[2] = a;
m[3] = a;
m[1]->Log(); // "Log A"
delete a;
a = NULL;
m[1]->Log(); // "Log A"
return 0;
}
输出:
记录A
记录A
记录A
我的问题:
m[1]->Log()
之后调用delete a
不会抛出异常只是偶然吗?A
实例的所有条目的最佳方法是什么?我的意思是我希望删除m.find(1)
后所有m.find(2)
,m.find(3)
和m.end()
都返回a
。任何建议都将不胜感激。答案 0 :(得分:5)
是和否。从技术上讲,它是未定义的行为,但通常(不依赖于此)调用不访问成员的非虚方法似乎适用于大多数编译器,因为大多数编译器都实现此调用没有解除引用this
(这是无效部分)。所以,在标准看来,这是偶然的。对于大多数编译器来说,它是预期的(或者至少是处理函数调用的副作用)。
请改用智能指针。要删除元素,您可以遍历地图并将每个值与您的值进行比较。到达时,请使用erase
。 擦除后迭代器无效。
答案 1 :(得分:4)
取消引用已删除对象时发生的任何事情都是未定义的行为,因此即使获得异常也可被视为“偶然”
最好的方法是将指向的对象的删除与您拥有句柄的生命周期相结合。因此,在您的情况下,如果从地图中删除指针,则可以确定删除对象是最佳的。要实现这一点,您可以使用int的映射到 std::unique_ptr<A>
而不是原始指针之一。
修改:更详细地查看要求后:
现在,既然你想要删除映射类型指向已删除对象的元素,并且无法确定指针指向的内存是否已被删除,我看不到从地图中删除这些条目的简单方法除了在一个函数调用中完成所有操作。由于std::map
等人不喜欢std::remove_if
,因此可以使用循环:
template <typename T1, typename T2>
void removeEntriesAndDelete(std::map<T1, T2*>& m, T2*& item) {
for (auto i = m.begin(); i != m.end(); ) {
if ( item == i->second) {
m.erase(i++);
} else {
++i;
}
}
delete item;
item=0;
}
int main() {
A* a = new A;
std::map<int,A*> m;
m[1] = a;
m[2] = a;
m[3] = a;
std::cout << std::boolalpha;
std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";
removeEntriesandDelete(m, a);
std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";
}
答案 2 :(得分:2)
通常,在某处注册的对象必须通知对象在哪里 他们报名参加。在简单的情况下,例如你的示例代码 通过地图比较简单,擦除每个元素 指向您的对象,但我认为您的实际用例较少 不重要的。通常的解决方案(事实上,唯一真正有效的解决方案) 在实践中)涉及观察者模式;当一个对象保存一个 指向您的对象的指针,直接或在地图或列表中 无论如何,它还会与您的对象一起使用,请求通知何时 你的物体被毁坏了。你的对象保留了这些观察者的列表, 并将在其析构函数中通知它们。应用于一个简单的案例 你的,它看起来像很多不必要的代码,但在上下文中 发生这种模式的实际应用程序,并没有那么多。
答案 3 :(得分:1)
当你提到它时,运气很好。
如前所述使用智能指针之类的东西,或者在类中封装地图和值处理,您可以在其中使用方法从地图中删除对象和删除对象。