Всегда ли вызывается деструктор для оператора удаления, даже если он перегружен? - PullRequest
3 голосов
/ 16 ноября 2009

Я портирую немного старого кода с C на C ++. Старый код использует объектно-подобную семантику, и в какой-то момент уничтожение объекта отделяет от освобождения неиспользуемой памяти, между которыми stuff происходит:

Object_Destructor(Object *me) { free(me->member1), free(me->member2) }

ObjectManager_FreeObject(ObjectManager *me, Object *obj) { free(obj) }

Возможна ли вышеуказанная функциональность в C ++ с использованием стандартного деструктора (~Object) и последующего вызова delete obj? Или, как я боюсь, выполнение этого вызовет деструктор дважды?

В частном случае operator delete из Object также переопределяется. Является ли определение, которое я читал в другом месте («когда используется оператор delete и объект имеет деструктор, деструктор всегда вызывается), правильно в случае переопределенного оператора?

Ответы [ 9 ]

6 голосов
/ 16 ноября 2009

delete operator используется для освобождения памяти, она не меняет, вызывается деструктор или нет. Сначала вызывается деструктор, и только после этого delete operator используется для освобождения памяти.

Другими словами, невозможно достичь семантики, к которой вы стремитесь, с помощью деструкторов C ++ и операторов удаления.

Sample

#include <iostream>
#include <new>
using namespace std;

struct foo {
    ~foo() { cout << "destructor\n"; }
    void operator delete(void* p) { 
        cout << "operator delete (not explicitly calling destructor)\n"; 
        free(p);
        cout << "After delete\n"; 
    }
};

int main()
{
    void *pv = malloc(sizeof(foo));
    foo* pf = new (pv) foo; // use placement new
    delete pf;
}

Выход:

деструктор

оператор delete (явно не вызывающий деструктор)

После удаления

3 голосов
/ 16 ноября 2009

Перегружено delete по-прежнему неявно вызывает деструктор, прежде чем он начнет выполняться, в отличие от удаления размещения (но удаление размещения не должно вызываться напрямую). Так что, если вы собираетесь «удалить» объект, не уничтожайте его заранее, у вас будет дважды вызван деструктор. Однако явное уничтожение происходит, если объект был создан с размещением нового (но в этом случае вы не уничтожаете объект, используя delete)

2 голосов
/ 17 ноября 2009

Я абсолютно не понимаю, почему люди говорят, что это невозможно.

Отсоединение инициализации от построения и обнуления (tm) от разрушения на самом деле очень просто.

class Clike
{
public:
  Clike() : m_usable(true) {}

  void clear(); // performs clean up then sets m_usable to false
  ~Clike() { if (m_usable) this->clear(); }

private:
  bool m_usable;
  // real variables
};

Тогда вы можете использовать его так:

Clike* c = new Clike();

c->clear(); // performs cleanup

// stuff

delete c;

На самом деле, поскольку деструкторы никогда не должны выбрасывать и ничего не возвращать, нет ничего необычного в том, что cleanup и destruction разделяются так, что операция cleanup может выдавать ошибки Особенно для сложных зверей, таких как соединения с БД и т. Д. *

Хотя это и не «деструктор», он, безусловно, работает, и поэтому представленный C-код на самом деле прекрасно воспроизводится без такого необычного размещения и т. Д. *

2 голосов
/ 16 ноября 2009

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

Если вам нужно воспроизвести семантику, используйте функцию-член, которая освобождает все ресурсы, и используйте ее вместо функции destruct. Убедитесь, что функцию можно вызывать более одного раза безопасно, и просто для уверенности включите ее в деструктор C ++.

1 голос
/ 16 ноября 2009

Нет, это невозможно.

delete вызывает деструктор.

Вам потребуется выработать какую-то логику, чтобы убедиться, что все происходит в правильном порядке.

1 голос
/ 16 ноября 2009

Вы можете отделить уничтожение от удаления, но вы, вероятно, не очень хотите.

Если вы выделите память с помощью new char[] или malloc, а затем вызовете размещение нового, тогда вы можете отделить уничтожение (что вы делаете, напрямую вызывая деструктор) от удаления (или free). Но тогда вы больше не вызываете перегруженный класс operator delete, вместо этого вы вызываете delete[] для массива char (или free).

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

0 голосов
/ 17 ноября 2009

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

Тем не менее, просто сделайте object_destructor () функцией-членом и вызывайте ее явно (и обычно это хороший стиль, чтобы сделать его безопасным для повторного вызова. Однако в вашем случае вызов дважды является нормальным, так как вызов free () с Указатель NULL является допустимым, поэтому альтернативная версия object_destructor просто должна подчеркнуть, как это можно сделать.

CLASS A {
   object_destructor() { free(this->member1); free(this->member2); }

   alternate_version_of_object_destructor() {  
           if (this->member1) { free(this->member1); this->member1= NULL; } 
           if (this->member2) { free(this->member2); this->member2= NULL; } }

    ~A() { /* do nothing or just call this->object_destructor() for safety */ }
}


foo() {
    A *pa= new A;


    pa->object_destructor();  /* member cleanup */
    delete pa;                /* free object memory */
} 
0 голосов
/ 16 ноября 2009

Посмотрите на std :: allocators для реализации. Ответ «да, они могут быть отделены». Это довольно легко сделать, просто это не часто можно увидеть.

0 голосов
/ 16 ноября 2009

Звучит так, как будто вы захотите разместить новое . С другой стороны, это также звучит так, как будто ваш код становится довольно волосатым. Это может быть время для серьезного рефакторинга.

...