Вызвать деструктор, а затем конструктор (сброс объекта)
Автор вопроса: CommunityОтветов: 10
Я хочу сбросить объект. Могу ли я сделать это следующим образом?
anObject->~AnObject();
anObject = new(anObject) AnObject();
// edit: this is not allowed: anObject->AnObject();
Этот код, очевидно, является подмножеством типичного жизненного цикла объекта, выделенного в месте размещения new:
AnObject* anObject = malloc(sizeof(AnObject));
anObject = new (anObject) AnObject(); // My step 2.
// ...
anObject->~AnObject(); // My step 1.
free(anObject)
// EDIT: The fact I used malloc instead of new doesn't carry any meaning
Единственное, что изменилось, это порядок вызовов конструктора и деструктора.
[11.9] Но могу ли я явно вызвать деструктор, если я разместил свой объект с помощью new?
FAQ: Нельзя, если только объект не был выделен с размещением new. Объекты, созданные new, должны быть удалены, что делает две вещи (запомните их): вызывает деструктор, а затем освобождает память.
FQA: Перевод: удаление - это способ явного вызова деструктора, но он также освобождает память. Вы также можете вызвать деструктор без освобождения памяти. В большинстве случаев это некрасиво и бесполезно, но вы можете это сделать.
Вызов деструктора/конструктора, очевидно, является обычным кодом C++. Гарантии, используемые в коде, являются прямым результатом размещения новых гарантий. Это ядро стандарта, это надежная вещь. Как его можно назвать "грязным" и представить чем-то ненадежным?
Как вы думаете, возможно ли, что реализация new на месте и без места отличается? Я думаю о какой-то больной возможности, что обычный новый может, например, указать размер блока памяти, выделенного перед блоком, чего, очевидно, новый на месте не сделает (потому что он не выделяет никакой памяти). Это может привести к возникновению пробела для некоторых проблем... Возможна ли такая реализация new()?
Люди говорят вам, что вы не можете вызвать конструктор. Честно говоря, я никогда-никогда не видел конструктора, вызываемого как в anObject-›AnObject();, но я не знаю стандарта наизусть и не искал его, чтобы подтвердить, что он запрещен. Однако, если вы так уверены, что это ядро стандарта и незыблемая, почему вы спрашиваете? Если вы уверены, это означает, что вы протестировали свой код и он работает, поэтому, если вам нравится это решение, просто используйте его. Daniel Daranas 14.07.2009
Я хочу убедиться, что я не пропустил ничего подобного последнему редактированию в вопросе. 14.07.2009
Не ведитесь на тролля FQA. Он, как обычно, ошибается в фактах.
Конечно, вы можете вызвать деструктор напрямую для всех объектов, независимо от того, созданы они с новым размещением или нет. Некрасиво в глазах смотрящего, оно действительно редко требуется, но единственный неприятный факт заключается в том, что и выделение памяти, и создание объектов должны быть сбалансированы.
"Обычное" новое/удаление немного упрощает это, связывая выделение памяти и создание объекта вместе, а выделение стека упрощает его еще больше, делая и то, и другое за вас.
Однако совершенно законно следующее:
int foo() {
CBar bar;
(&bar)->~CBar();
new (&bar) CBar(42);
}
Оба объекта уничтожаются, а память стека также автоматически перерабатывается. однако, в отличие от утверждений FQA, первому вызову деструктора не предшествует размещение new.
Ответ 2 · рейтинг 21 · Community
Почему бы не реализовать метод Clear(), который делает все, что делает код в теле деструктора? Затем деструктор просто вызывает Clear(), а вы вызываете Clear() непосредственно для объекта, чтобы "сбросить его".
Другой вариант, если ваш класс правильно поддерживает назначение:
MyClass a;
...
a = MyClass();
Я использую этот шаблон для сброса экземпляров std::stack, поскольку адаптер стека не предоставляет четкую функцию.
Ответ 3 · рейтинг 14 · Brock Woolf
С технической точки зрения явный вызов конструкторов или деструкторов является плохой практикой.
Ключевое слово удаления вызывает их, когда вы его используете. То же самое касается new с конструкторами.
Извините, но этот код заставляет меня рвать на себе волосы. Вы должны делать это так:
Выделить новый экземпляр объекта
AnObject* anObject = new AnObject();
Удалить экземпляр объекта
delete anObject;
НИКОГДА не делайте этого:
anObject->~AnObject(); // My step 1.
free(anObject)
Если вы должны "сбросить" объект, либо создайте метод, который очищает все переменные экземпляра внутри, либо я бы порекомендовал вам удалить объект и выделить себе новый.
"Это ядро языка?"
Это ничего не значит. В Perl есть около шести способов написания цикла for. Тот факт, что вы МОЖЕТЕ делать что-то на языке, потому что он поддерживается, означает, что вы должны его использовать. Черт возьми, я мог бы написать весь свой код, используя операторы for switch, потому что "ядро" языка поддерживает их. Не делает это хорошей идеей.
Почему вы используете malloc, когда вам это явно не нужно. Malloc - это метод C.
Создать и удалить - ваши друзья в C++
"Сброс" объекта
myObject.Reset();
Ну вот. Таким образом, вы избавляетесь от ненужного выделения и освобождения памяти опасным образом. Напишите свой метод Reset(), чтобы очистить значение всех объектов внутри вашего класса.
Ответ 4 · рейтинг 9 · Konrad Rudolph
Вы не можете вызвать конструктор указанным вами способом. Вместо этого вы можете сделать это, используя новое размещение (как также указывает ваш код):
new (anObject) AnObject();
Этот код гарантированно будет хорошо определен, если ячейка памяти все еще доступна - как и должно быть в вашем случае.
(Я удалил часть о том, является ли этот код дискуссионным или нет - он четко определен. Точка.)
Кстати, Брок прав: то, как реализация delete не исправлена - это не то же самое, что и вызов деструктора, за которым следует free. Всегда соединяйте вызовы new и delete, никогда не смешивайте один с другим: это не определено.
Ответ 5 · рейтинг 7 · avakar
Обратите внимание, что используются не malloc и free, а operator new и operator delete. Кроме того, в отличие от вашего кода, используя new, вы гарантируете безопасность исключений. Почти эквивалентный код будет следующим.
Предлагаемая вами перезагрузка действительна, но не идиоматична. Это трудно понять правильно, и поэтому это обычно не одобряется и не одобряется.
Ответ 6 · рейтинг 5 · Rakete1111
Да, то, что вы делаете, действительно в большинстве случаев. [basic.life]p8 говорит:
Если после того, как время жизни объекта закончилось и до того, как хранилище, которое занимал объект, было повторно использовано или освобождено, новый объект создается в месте хранения, которое занимал исходный объект, указатель, указывающий на исходный объект, ссылка, которая относится к исходному объекту, или имя исходного объекта будет автоматически ссылаться на новый объект и, как только начнется время жизни нового объекта, может использоваться для управления новым объектом, если:
хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
новый объект имеет тот же тип, что и исходный объект (игнорируя cv-квалификаторы верхнего уровня), и
тип исходного объекта не является константным, и, если тип класса, не содержит нестатических элементов данных, чей тип является константным или ссылочным типом, и
ни исходный объект, ни новый объект не являются потенциально перекрывающимися подобъектами ([intro.object]).
Так что законно, если у вас нет const или ссылочного члена.
Если у вас нет этой гарантии, вам нужно использовать std::launder или использовать указатель, возвращаемый размещением new (как вы все равно делаете), если вы хотите использовать новый объект:
// no const/ref members
anObject->~AnObject(); // destroy object
new (anObject) AnObject(); // create new object in same storage, ok
anObject->f(); // ok
// const/ref members
anObject->~AnObject();
auto newObject = new (anObject) AnObject();
anObject->f(); // UB
newObject->f(); // ok
std::launder(anObject)->f(); // ok
Ответ 7 · рейтинг 4 · Logan Capaldo
Вы не можете вызвать конструктор таким образом, но нет ничего плохого в повторном использовании памяти и вызове нового размещения, если вы не удаляете память (освобождаете). Я должен сказать, что сброс объекта, подобного этому, немного схематичен. Я бы написал объект, который можно явно сбрасывать, или написал бы метод подкачки и использовал его для его сброса.
E.g.
anObject.swap( AnObject() ); // swap with "clean" object
Ответ 8 · рейтинг 4 · David Feurle
Почему бы не сбросить с помощью оператора = ()? Это не подлежит обсуждению и гораздо более читабельно.
A a;
//do something that changes the state of a
a = A(); // reset the thing
Ответ 9 · рейтинг 3 · EFraim
Если ваш объект имеет разумную семантику присваивания (и правильный оператор =), тогда *anObject = AnObject() имеет больше смысла и его легче понять.
Ответ 10 · рейтинг 1 · manovi
Гораздо лучше просто добавить что-то вроде метода Reset() к вашему объекту, а не играть с размещением new.
Вы используете новую функцию размещения, которая предназначена для того, чтобы позволить вам контролировать, где размещается объект. Обычно это проблема только в том случае, если ваше оборудование имеет "специальную" память, например флэш-чип. ЕСЛИ вы хотите поместить какие-либо объекты во флэш-чип, вы можете использовать эту технику. Причина, по которой он позволяет вам явно вызывать деструктор, заключается в том, что ВЫ теперь контролируете память, поэтому компилятор C++ не знает, как выполнить часть освобождения при удалении.
Это также не сэкономит вам много кода, с помощью метода сброса вам придется установить элементы в их начальные значения. malloc() этого не делает, поэтому вам все равно придется писать этот код в конструкторе. Просто создайте функцию, которая устанавливает вашим членам начальные значения, вызовите ее Reset(), вызовите ее из конструктора, а также из любого другого места, которое вам нужно.