Проблема деструктора C ++ - PullRequest
       16

Проблема деструктора C ++

1 голос
/ 03 февраля 2011

У меня странное поведение деструктора в 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 ]

3 голосов
/ 03 февраля 2011

Трудно увидеть без полного кода, однако я могу заметить, что вы нарушаете правило большой тройки (у вас есть деструктор, но нет конструктора копирования или оператора присваивания), и это означает, что нужно искать проблемы.

Мое предположение состоит в том, что вы создаете копию журнала или другие подобные проблемы, и как только вы входите в режим UB, может произойти все что угодно, включая появление ошибок в местах, которые должны быть в порядке.

1 голос
/ 03 февраля 2011

Вы храните голые указатели на карте Entries?Если это так, вы должны исследовать, используя boost::shared_ptr (или tr1::shared_ptr, если вы его получили).Это значительно упрощает управление хранилищем (например, вы можете удалить цикл for в TransactionLog::OnCommit() и просто вызвать Entries.clear().

1 голос
/ 03 февраля 2011

Как уже было сказано , вам не хватает копирующего ctor и оператора присваивания для TransactionLog. Вот упрощенная проблема:

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 и оператор присваивания для вашего класса. Даже если ваш класс не подлежит копированию, вы все равно должны объявить их, но сделать их закрытыми и не определять их:

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

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

Когда они закрытые, любое использование (в недоступном контексте) будет ошибкой компилятора.

0 голосов
/ 02 сентября 2011

здесь может быть 2 цента:

  1. , как было сказано ранее, старайтесь избегать использования голых указателей c / c ++ на объекты, вместо этого используйте какой-нибудь умный ptr (например, boost ::shared_ptr) или auto_ptr для простого удержания / освобождения ресурсов (но не в контейнерах stl из-за специфики auto_ptr)

  2. об этом сбое: ничто не мешает вам заполнить объект карты разными ключамино те же значения.так что становится возможным удалять объект дважды или более раз, что определенно приводит к сбою (вы можете удалить указатель дважды или более, если он равен 0).так напишите это:

    delete ptr;ptr = 0;

вместо простого удаления указателя

0 голосов
/ 03 февраля 2011

Глядя на то, что у вас есть в функции OnCommit:

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

delete entry;
...

похоже, вы проверяете, удалено ли что-то. Если это так, вы удаляете что-то внутри него и всегда удаляете объект снова. Похоже, вы спрашиваете о проблемах.

...