Вызывает ли деструктор явное уничтожение объекта полностью?

Если я вызываю деструктор явно (myObject. ~ Object()), это заверит меня, что объект будет соответствующим образом уничтожен (вызов всех дочерних деструкторов)?

Хорошо код:

class Object
{
   virtual ~Object()
   {}
};

class Widget : public Object
{
   virtual ~Widget()
   {}
};

...
Object* aWidget = new Widget(); //allocate and construct
aWidget->~Object(); //destroy and DON'T deallocate

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

Спасибо!

Ответ 1

Ответ... почти всегда.

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

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

ОДНАКО, вторая разделяемая библиотека не была перекомпилирована. Это означает, что он не знал о недавно добавленном определении виртуального объекта. Удалить, вызванное из второй разделяемой библиотеки, просто называется бесплатным и не вызывает виртуальную цепочку деструкторов. Результатом стала неприятная утечка памяти.

Ответ 2

Да. Но святые курят, вы уверены в этом? Если это так, я бы использовал размещение new, чтобы построить ваш Widget. Использование размещения new, а затем явно вызов деструктора является приемлемой, если это необычно, идиомой.

Изменить. Рассмотрим распределение памяти вручную, а не используя new, чтобы выделить первый объект, а затем снова использовать его память. Это позволяет вам полностью контролировать память; вы могли бы выделять большие куски за раз, например, вместо того, чтобы выделять отдельный блок памяти для каждого Widget. Это будет справедливая экономия, если память действительно является таким скудным ресурсом.

Кроме того, и, что еще более важно, вы тогда обычно выполняете размещение new ", а не это гибридное регулярное решение new/placement new. Я не говорю, что это не сработает, я просто говорю это скорее, ах, творческое решение проблемы с вашей памятью.

Ответ 3

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

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

Ответ 4

Да, он вызовет всех дочерних деструкторов, чтобы он работал так, как вы ожидаете.

Деструктор - это просто функция в конце концов, так получилось, что он вызывается, когда объекты удаляются.

Поэтому, если вы используете этот подход, будьте осторожны:

#include <iostream>

class A
{
public: 
    A(){};
    ~A()
    {
        std::cout << "OMG" << std::endl;
    }
};

int main()
{
    A* a = new A;
    a->~A();
    delete a;
    return 0;
}

output:
OMG
OMG 

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

Ответ 5

Пожалуйста, сохраните себе настоящие головные боли и используйте Boost Object Pool, который звучит как существующая реализация вашего шаблона источника/приемника. Он будет выделять большие куски памяти, нарезать их в правильный размер для вашего объекта и вернуть их вам (после вызова конструктора). Когда вы удаляете объекты, они вызывают их деструктор и помещаются в связанный список объектов для повторного использования. Он будет расти и автоматически сокращаться и гарантировать, что экземпляры ваших объектов имеют тенденцию быть близкими друг к другу в памяти.

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

Ответ 6

Да. Деструктор вызывает любые деструкторы участников в порядке LIFO, затем деструкторы базового класса, и нет способа предотвратить его вызов этими деструкторами *. Стек объекта гарантированно разматывается.

Инициализация и финализация отделены от выделения и освобождения памяти в С++ точно так, что при возникновении особого случая существует однозначный синтаксис, в котором прикладной программист может выразить свое намерение компилятору.

Edit:

  • Я полагаю, что, вызывая abort() или longjmp(), можно, по сути, предотвратить запуск деструкторов членов и базового класса.

Ответ 7

Запуск деструктора не освобождает память, используемую разрушаемым объектом - оператор delete делает это. Обратите внимание, однако, что деструктор может удалить "дочерние объекты", и их память будет освобождена, как обычно.

Вам необходимо прочитать о размещении new/delete, поскольку это позволяет вам управлять распределением памяти и запускать конструкторы/деструкторы.

См. здесь небольшую информацию:

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.9

Ответ 8

Контейнеры STL делают это. Фактически, распределитель STL должен предоставить метод destroy, который вызывает деструктор объекта (allcators также предоставляют метод deallocate для освобождения памяти, которая использовалась для хранения объекта). Однако совет Stroustrup (язык программирования С++ 10.4.11) -

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

Ответ 9

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

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

Плюс, если я ошибаюсь, вызов деструктора вручную - это единственный вариант, который у вас есть, если вы использовали новое размещение для вызова конструктора.

Ответ 10

Зачем уничтожать его вообще? Просто пишите по памяти? Вы хотите, чтобы логика выполнялась, чтобы грамотно обрабатывать выпуски ресурсов? Я намеренно заявляю, что это злоупотребление языком, а не хорошая идея.

Ответ 11

Я бы рассмотрел возможность переопределения нового для объектов, для которых требуется специальное распределение и поведение для освобождения - после всего, для чего это нужно. Гибридная схема нормальных новых и явно вызывающих деструкторов звучит как рецепт будущих головных болей. Для начала любая стратегия обнаружения утечки памяти будет отброшена.