2

所以这是我正在处理的代码:

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

我的问题:

  1. m[1]->Log()调用之后不会抛出异常只是偶然delete a吗?
  2. 擦除映射中指向已删除实例的所有条目的最佳方法是A什么?我的意思是我要全部m.find(1)m.find(2)并在删除后m.find(3)返回。任何意见,将不胜感激。m.end()a
4

4 回答 4

5
  1. 是和不是。从技术上讲,这是未定义的行为,但通常(不要依赖于此)调用不访问成员的非虚拟方法似乎适用于大多数编译器,因为大多数编译器在没有取消引用的情况下实现此调用this(这是无效部分)。因此,在标准看来,这是偶然的。对于大多数编译器来说,这是有意的(或者至少是处理函数调用的副作用)。

  2. 请改用智能指针。要删除元素,您可以遍历地图并将每个值与您的值进行比较。当你达到一个时,使用erase.迭代器在擦除后失效。

于 2012-05-29T12:30:08.627 回答
4
  1. 取消引用已删除对象时发生的任何事情都是未定义的行为,因此即使出现异常也可能被视为“偶然”

  2. 最好的方法是将删除指向的对象与您所处理的对象的生命周期结合起来。因此,在您的情况下,您可以决定如果从地图中删除指针,则最好删除对象。为此,您可以使用 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";
}
于 2012-05-29T12:33:43.990 回答
2

通常,在某处登记的对象必须通知它们登记的对象。在像您的示例代码这样的简单情况下,浏览地图相对简单,擦除指向您的对象的每个元素,但我假设您的实际用例不那么琐碎。通常的解决方案(事实上,唯一在实践中真正有效的解决方案)涉及观察者模式;当一个对象直接或在地图或列表或其他任何内容中保存指向您的对象的指针时,它也会与您的对象一起注册,当您的对象被破坏时请求通知。您的对象保留了这些观察者的列表,并将在其析构函数中通知它们。应用于像您这样的简单案例,它看起来像很多不必要的代码,但在出现这种模式的实际应用程序的上下文中,它并没有那么多。

于 2012-05-29T13:46:58.313 回答
1

正如你提到的,它靠运气起作用。

要么使用前面提到的智能指针之类的东西,要么将映射和值处理封装在一个类中,在那里你可以有一个从映射中删除对象删除对象的方法。

于 2012-05-29T12:32:26.377 回答