Является ли память выпущена при вызове деструктора или когда вызывается `delete`?
Предположим, что у вас есть объект class Fool
.
class Fool
{
int a,b,c;
double* array ;
//...
~Fool()
{
// destroys the array..
delete[] array ;
}
};
Fool *fool = new Fool() ;
Теперь Я знаю, что вы не должны, но какой-то дурак называет деструктор на fool
в любом случае. fool->~Fool();
.
Означает ли это, что память fool
освобождена (т.е. a, b, c недопустимы), или это означает только то, что происходит дезаллокация в функции ~Fool()
(т.е. массив удаляется только?)
Итак, я думаю, мой вопрос в том, является ли деструктор просто другой функцией, вызываемой при вызове delete
на объект, или он делает больше?
Ответы
Ответ 1
Если вы пишете
fool->~Fool();
Вы завершаете время жизни объекта, которое вызывает деструктор и восстанавливает внутренний массив array
. Однако память, удерживающая объект, не освобождается, а это означает, что если вы хотите вернуть объект к жизни с помощью нового места размещения:
new (fool) Fool;
вы можете это сделать.
В соответствии со спецификацией, чтение или запись значений полей fool
после явного вызова результата деструктора в поведение undefined, поскольку срок жизни объекта завершен, но память, удерживающая объект, все еще должна быть выделена и вам нужно освободить его, вызвав operator delete
:
fool->~Fool();
operator delete(fool);
Причина использования operator delete
вместо написания
delete fool;
заключается в том, что последний имеет поведение undefined, потому что fool
время жизни уже завершено. Использование программы необработанного разворота operator delete
гарантирует, что память будет восстановлена, не пытаясь сделать что-либо, чтобы положить конец жизни объекта.
Конечно, если память для объекта не получена из new
(возможно, она выделена стекем или, может быть, вы используете настраиваемый распределитель), то вы не должны использовать operator delete
, чтобы освободить ее, Если вы это сделали, вы получите поведение undefined (снова!). Кажется, это повторяющаяся тема в этом вопросе.: -)
Надеюсь, это поможет!
Ответ 2
Деструкторный вызов делает именно это, он вызывает деструктор. Не больше, не меньше. Выделение отделено от строительства и освобождения от разрушения.
Типичная последовательность:
1. Allocate memory
2. Construct object
3. Destroy object (assuming no exception during construction)
4. Deallocate memory
Фактически, если вы запустите это вручную, вам придется вызвать деструктор самостоятельно:
void * addr = ::operator new(sizeof(Fool));
Fool * fp = new (addr) Fool;
fp->~Fool();
::operator delete(addr);
Автоматический способ записи это, конечно, Fool * fp = new Fool; delete fp;
. Выражение new
вызывает выделение и построение для вас, а выражение delete
вызывает деструктор и освобождает память.
Ответ 3
Означает ли это, что память обмана освобождена (т.е. a, b, c недействительны) или делает что означает только то, что происходит с деллалокации в функции ~ Fool() (т.е. массив удаляется только?)
Fool::~Fool()
имеет нулевое знание о том, хранится ли экземпляр Fool
в динамическом хранилище (через new
) или он хранится в автоматическом хранилище (т.е. объекты стека). Поскольку объект перестает существовать после запуска деструктора, вы не можете предположить, что a
, b
, c
и array
останутся действительными после выхода деструктора.
Однако, поскольку Fool::~Fool()
ничего не знает о назначении Fool
, вызов деструктора непосредственно на new
-allocated Fool
не освободит базовую память, которая поддерживает объект.
Ответ 4
Вам не следует обращаться к a
, b
и c
после вызова деструктора, даже если это явный вызов деструктора. Вы никогда не знаете, что ваш компилятор помещает в ваш деструктор, который может сделать эти значения недействительными.
Тем не менее, память не освобождается в случае явного вызова деструктора. Это по дизайну; он позволяет очистить объект, построенный с использованием размещения new
.
Пример:
char buf[sizeof (Fool)];
Fool* fool = new (buf) Fool; // fool points to buf
// ...
fool->~Fool();
Ответ 5
Самое простое место, чтобы увидеть, что деструктор отличается от освобождения через delete
, - это когда автоматическое распределение в первую очередь:
{
Fool fool;
// ~Fool called on exit from block; nary a sign of new or delete
}
Обратите внимание, что контейнеры STL в полной мере используют явный вызов деструктора. Например, std::vector<>
относится к хранению и содержанию жизни объекта совершенно отдельно.