Вызов удаления переменной, выделенной в стеке
-
22-07-2019 - |
Вопрос
Не обращая внимания на стиль и дизайн программирования, «безопасно» ли вызывать удаление переменной, выделенной в стеке?
Например:
int nAmount;
delete &nAmount;
или
class sample
{
public:
sample();
~sample() { delete &nAmount;}
int nAmount;
}
Решение
Нет, звонить небезопасно delete
в переменной, выделенной в стеке.Вам следует позвонить только delete
о вещах, созданных new
.
- Для каждого
malloc
илиcalloc
, там должен быть ровно одинfree
. - Для каждого
new
там должен быть ровно одинdelete
. - Для каждого
new[]
там должен быть ровно одинdelete[]
. - Для каждого выделения стека не должно быть явного освобождения или удаления.Деструктор вызывается автоматически, если это применимо.
В общем, вы не можете смешивать и сочетать что-либо из этого, например.нет free
-инг или delete[]
-ing a new
объект.Это приводит к неопределенному поведению.
Другие советы
Что ж, давайте попробуем:
jeremy@jeremy-desktop:~$ echo 'main() { int a; delete &a; }' > test.cpp
jeremy@jeremy-desktop:~$ g++ -o test test.cpp
jeremy@jeremy-desktop:~$ ./test
Segmentation fault
Так что, видимо, это совсем не безопасно.
Имейте в виду, что когда вы выделяете блок памяти с помощью new (или malloc, если на то пошло), фактически выделенный блок памяти будет больше, чем вы запросили.Блок памяти также будет содержать некоторую бухгалтерскую информацию, поэтому, когда вы освободите блок, его можно будет легко вернуть в свободный пул и, возможно, объединить с соседними свободными блоками.
Когда вы попытаетесь освободить любую память, которую вы не получили из новой, этой бухгалтерской информации там не будет, но система будет вести себя так, как есть, и результаты будут непредсказуемыми (обычно плохими).
Да, это неопределенное поведение:переходя к delete
все, что не пришло из new
это УБ:
Стандарт C++, раздел 3.7.3.2.3:Значение первого аргумента, передаваемого одной из функций освобождения, предусмотренных в стандартной библиотеке, может быть
null
значение указателя;если да, и если функция освобождения предоставляется в стандартной библиотеке, вызов функции освобождения не имеет никакого эффекта.В противном случае значение, передаваемое вoperator delete(void*)
в стандартной библиотеке должно быть одно из значений, возвращаемых предыдущим вызовом любого изoperator new(std::size_t)
илиoperator new(std::size_t, const std::nothrow_t&)
в стандартной библиотеке.
Последствия неопределенного поведения, ну, неопределенны.«Ничего не происходит» — такое же значимое следствие, как и все остальное.Однако обычно «сразу ничего не происходит»:освобождение недопустимого блока памяти может иметь серьезные последствия при последующих вызовах распределителя.
Немного поигравшись с g++ 4.4 в Windows, я получил очень интересные результаты:
вызов удаления переменной стека, похоже, ничего не делает.Ошибок не возникает, но после удаления я могу без проблем получить доступ к переменной.
Имея класс с методом с
delete this
успешно удаляет объект, если он выделен в куче, но не если он выделен в стеке (если он есть в стеке, ничего не происходит).
Никто не может знать, что происходит.Это вызывает неопределенное поведение, поэтому может произойти буквально все, что угодно. Не делай этого.
Нет, память, выделенная с использованием нового, должна быть удалена с использованием оператора Delete, и что выделено с использованием Malloc должна быть удалена с помощью бесплатного.И нет необходимости освобождать переменную, выделенную в стеке.
Ангел теряет крылья...Вы можете только позвонить delete
по указателю, выделенному с помощью new
, в противном случае вы получите неопределенное поведение.
здесь память выделяется с помощью стека, поэтому нет необходимости удалять ее внешне, но если вы выделили динамически
как int *a = new int ()
тогда вам нужно удалить a, а не удалять &a (a само по себе является указателем), потому что память выделяется из свободного хранилища.
Вы уже сами ответили на вопрос. delete
должен использоваться только для указателей, полученных через new
.Делать что-либо еще — это простое неопределенное поведение.
Поэтому на самом деле невозможно сказать, что произойдет: все, от нормально работающего кода до сбоя и стирания вашего жесткого диска, является действительным результатом этого.Поэтому, пожалуйста никогда не делай этого.
Это UB, потому что вы не должны вызывать delete для элемента, который не был динамически выделен с помощью new.Это так просто.