Подтвердить что ты не робот

Об использовании новых и удалить, а также рекомендации Страуступа

Об использовании новых и удаления, а также рекомендации Stroustrup...

Он говорит что-то вроде (но не совсем, это из моих заметок его книги):

Правило большого пальца состоит в том, что new принадлежит конструкторам и аналогичным операциям, delete принадлежит деструкторам. Кроме того, new часто используется в аргументах для дескрипторов ресурсов. В противном случае не используйте new и delete, вместо этого используйте дескрипторы ресурсов (интеллектуальные указатели).

Мне было интересно, действительно ли применяли это более опытные люди с С++ 11.

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

4b9b3361

Ответ 1

Это великое правило. Фактически, вы можете избежать использования new в аргументах для интеллектуальных указателей, используя соответствующие функции make_. Например, вместо:

std::shared_ptr<int> p(new int(5));

Вы можете часто делать:

auto p = std::make_shared<int>(5);

Это также имеет преимущество быть более безопасным. Пока std::make_unique еще не существует, он планирует пробиться в С++ 14 (он уже находится в рабочем черновике). Если вы хотите его сейчас, существуют некоторые существующие реализации.

Вы можете пойти дальше и даже избегать использования new и delete в конструкторах и деструкторах. Если вы всегда переносите динамически выделенные объекты в интеллектуальные указатели, даже если они являются членами класса, вам вообще не нужно будет управлять собственной памятью. См. Правило нуля. Идея заключается в том, что ответственность за реализацию какой-либо формы семантики (SRP не несет ответственность за ваш класс) - это то, для чего нужны интеллектуальные указатели. Тогда вам теоретически никогда не придется писать конструкторы копирования/перемещения, копировать/перемещать операторы присваивания или деструкторы, потому что неявно определенные функции обычно будут делать соответствующую вещь.

Ответ 2

Похоже, что вопрос больше, чем вопрос, но вот он: в коде приложения я вообще вообще не использую new. Из-за наших правил кодирования код действительно использует указатель, но ни один из этих "голых" указателей фактически не передает права собственности. Все объекты принадлежат другому объекту.

Чтобы быть справедливым, когда объекты должны быть выделены, распределение обычно использует что-то нравственно эквивалентное std::make_shared<T>(...), которое иногда появляется в коде приложения. Одной из основных причин этого довольно полного отсутствия new (или аналогичного) является то, что объекты обычно распределяются с использованием генераторов с использованием состояния и не делают этого через диспетчер ресурсов, на самом деле это довольно сложно. Таким образом, для прямого распределения памяти мало места, используя new или версию размещения в коде приложения.

В некотором коде инфраструктуры, особенно при создании пользовательских контейнеров, ситуация несколько отличается: там выделена память (из распределителей и инициализирована с помощью размещения new). Тем не менее, даже любой результат от распределения памяти и инициализации объектов немедленно передается менеджерам ресурсов. В принципе, я не могу справиться с явным управлением ресурсами, и использование менеджеров ресурсов просто облегчает мне необходимую работу.

Ответ 3

Я думаю, что каждый ресурс должен принадлежать чему-то. Владелец - тот, кто несет ответственность за очистку. Обычно этот владелец является умным указателем, но даже std::vector является владельцем ресурса: блок памяти, который хранит его смежные элементы. Этот совет относится не только к памяти, но и к любому ресурсу, например файловым дескрипторам, дескрипторам баз данных, мьютексам и т.д.

При вызове new и удалении вручную в некоторой части вашего кода, который не является классом-оболочкой, вы, программист, становитесь владельцем ресурса. Собственность несет ответственность за очистку после себя. Теперь вы и все программисты по обслуживанию, которые приходят после вас, должны убедиться, что все пути кода после нового в конечном итоге приводят к удалению. Даже для простых функций это очень легко ошибиться. С исключениями, почти невозможно, если вы не тщательно обернете все в блоках catch catch, в результате чего будут выполняться штрафы за производительность во время выполнения и загрязнение вашего кода дополнительными областями и ненужной логикой исключения. Наконец, даже если вы все исправите, вы просто потратили много времени на выполнение этой утомительной работы по управлению ресурсами. Компилятор - это инструмент, который может выполнять эту работу за вас, использовать его.

Худшая ситуация заключается в том, что какая-то подсистема выделяет ресурс, его пропускают вокруг приложения, а какая-то другая удаленная подсистема освобождает ее. Количество возможных кодовых путей в этой ситуации невозможно. Очень трудно, если не невозможно, чтобы человек рассуждал и доверял. На мой взгляд, этот стиль программирования недостижим. Сколько проектов C вы работали в прошлом, которые пронизаны ошибками памяти, особенно редко, если не выполнялись пути обработки ошибок? Я имел дело с больше, чем я хотел бы видеть больше.

C имеет ручное управление памятью, Java и другие - сбор мусора. С++ имеет RAII. Он настолько эффективен, как C, и почти так же безопасен, как сбор мусора.

Мое правило прост, если вы вручную очищаете любой ресурс, вы только что написали ошибку.