C ++析构函数问题

时间:2011-02-03 12:55:32

标签: c++ destructor

我在C ++中有一个奇怪的'析构函数'行为

以下是我调用的代码:

_log->OnCommit();
delete _log;

问题是当我调用“delete _log;”时它崩溃是因为变量'Entries'无效!!!!

你知道为什么吗?

以下是我的课程代码:

struct TransactionLogEntry {
    DependencyObject* Object;
    bool IsAttached;
    bool IsDeleted;
    bool IsUpdated;
};

class TransactionLog
{
public:
    TransactionLog();
    ~TransactionLog();

    void OnCommit();

    map<DependencyObject*, TransactionLogEntry*> Entries;
};

void TransactionLog::OnCommit()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        if (entry->IsDeleted)
            delete entry->Object;

        delete entry;
    }

    Entries.clear();
}

TransactionLog::~TransactionLog()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        delete entry;
    }

    Entries.clear();
}

5 个答案:

答案 0 :(得分:3)

如果没有完整的代码,很难看到,但我可以注意到你违反了三巨头的规则(你有一个析构函数,但没有复制构造函数或赋值运算符),这意味着要找麻烦。

我猜测你是复制构建日志或其他类似问题,一旦你进入UB模式,就会发生任何事情,包括在应该没问题的地方引发错误。

答案 1 :(得分:1)

As said,您错过了TransactionLog的复制ctor和赋值运算符。这是问题,简化:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }
}

这适当地分配和销毁一个对象,对吧?不完全:

void example() {
  A a;
  A b = a;  // Missing copy ctor; could also happen when passing/returning by value.
  b = a;  // Same result through missing assignment operator.
  assert(a.p == b.p);  // Here is the problem!
  // When b is destroyed, it deletes the pointer.
  // When a is destroyed, it attempts to delete the deallocated pointer,
  // leading to undefined behavior.
}

以下是与您的代码更紧密地编写相同的问题:

struct A {
  map<string, int*> m;
  A() {
    m["abc"] = new int();
  }
  ~A() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
  }
  void on_commit() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
    m.clear();
  }
}

解决方案是为您的类声明一个复制ctor和赋值运算符。即使你的课程是'不可复制的'#34;,你仍然应该声明它们,但要将它们设为私有且不要定义它们:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }

private:
  A(A const&);
  A& operator=(A const&);
}

当它们是私有的时,任何使用(在不可访问的上下文中)都将是编译器错误。

答案 2 :(得分:1)

您是否在Entries地图中存储了裸指针?如果是这样,您应该使用boost::shared_ptr(或tr1::shared_ptr,如果有的话)进行调查。这极大地简化了存储管理(例如,您可以删除for中的TransactionLog::OnCommit()循环,只需拨打Entries.clear()

答案 3 :(得分:0)

查看OnCommit功能中的内容:

...
if (entry->IsDeleted)
    delete entry->Object;

delete entry;
...

看起来你检查是否有东西被删除了。如果是这样,您删除其中的内容并始终再次删除该对象。看起来你在问问题。

答案 4 :(得分:0)

这可能是2美分:

  1. 如前所述,尽量避免使用裸c / c ++指针指向对象,而是使用某种智能ptr(例如boost :: shared_ptr)或auto_ptr来简单保存/释放资源(但不是stl容器由于auto_ptr特定)

  2. 关于此崩溃:没有什么可以阻止您使用不同的键但相同的值填充地图对象。所以有可能删除对象两次或更多次,这肯定会导致崩溃(如果它等于0,你可以删除指针两次或更多次)。所以写下:

    删除ptr; ptr = 0;

  3. 而不只是删除指针