Страницы

Поиск по вопросам

среда, 29 января 2020 г.

Нужно ли при удалении проверять, была ли выделена память?

#cpp


Есть указатель на объект, и он указывает на выделенную память через new. Можно спокойно
вызывать для него delete? А если он не указывает на созданный в динамической памяти
объект, то delete ошибку выполнения не вызовет? Как проверить, что объект указывает
на выделенный участок который можно удалить? Просто если if(object != null) или (object)
и потом аж вызывать delete? Или delete и сам может определить, есть ли что-то для удаления
или нет?    


Ответы

Ответ 1



Если указатель указывает на объект, выделенный при помощи new, и для него ещё ни разу не был вызван delete, то вызывать delete можно. Также можно вызывать delete по нулевому указателю (сколько угодно раз). В любом другом случае — нельзя. Ответственность за соблюдение этого условия лежит на программисте, ни компилятор, ни runtime-библиотека за вас этого не проверят. Хуже того, несоблюдение этого условия есть undefined behaviour: программа считается необратимо сломанной, и может вылететь, а может (что гораздо хуже) не вылететь, а работать как будто бы всё в порядке. Обычно помогает простой принцип: если вы сами выделили объект, вам его и удалять. Если к вам он пришёл со стороны, как правило ответственность за удаление лежит на том, кто его создавал. Но возможны и исключения, когда вместе с объектом вам передаётся и обязанность его удалить. Смотрите документацию функций, которые вы вызываете. С другой стороны, в современном C++ работа с «сырыми» указателями происходит довольно редко. Обычно между частями программы передаётся «умный» указатель (например, shared_ptr), при этом проблема снимается, поскольку объект по указателю автоматически удаляется, когда нужно.

Ответ 2



Стандартный вариант использования new/delete включает в себя манипуляции с установкой указателя при его инициализации в nullptr и после вызова delete в nullptr. Таким образом, если указатель указывает в nullptr, это является сигналом, что он не указывает на участок памяти. В противном случае указывает. Но это, скорее, вопрос стиля. Определить по указателю, указывает ли он на начало выделенной памяти через new или куда-то еще - невозможно (теоретически возможно, зная реализацию new и malloc, но это костыли и танцы с бубном, которые нельзя делать никогда). Хорошим сегодня стилем является использование vector< char > в качестве переменной для хранения буффера. Стандарт C++ гарантирует, что будет выделен один блок памяти. При этом работа через vector освобождает программиста от заботы об освобождении памяти. Кроме того, освобождает от проблем с exception как при выделении памяти, так и в случае, если exception будет выброшен позже, при сворачивании стэка выделенная под вектор память корректно освободится, что не произойдет, если она выделена через new. Издержки производительности не должны волновать до этапа оптимизации, и должны быть очень незначительными. Кроме того, важный момент про использование сторонних библиотек: поскольку варианты реализации alloc/free и new/delete могут разниться от компилятора к компилятору, удалять можно лишь то, что вы сами создали. Никакая память, выделенная в уже собранной библиотеке не должна освобождаться в ее клиенте и наоборот. UPD: в большей степени это относится к ОС семейства Windows.

Комментариев нет:

Отправить комментарий