Страуструп сделал несколько хороших комментариев по этому поводу на конференции Going Native 2013 года.
Просто перейдите к 25м50 в этом видео . (Я бы порекомендовал посмотреть все видео на самом деле, но это пропускает материал о сборке мусора.)
Если у вас действительно отличный язык, который позволяет легко (и безопасно, и предсказуемо, и легко читаться, и легко учить) работать с объектами и значениями прямым способом, избегая (явного) использования куча, тогда вам даже не нужна сборка мусора.
С современным C ++ и тем, что есть в C ++ 11, сборка мусора больше не нужна, за исключением ограниченных обстоятельств. Фактически, даже если хороший сборщик мусора встроен в один из основных компиляторов C ++, я думаю, что он не будет использоваться очень часто. Будет проще , а не сложнее избежать ГК.
Он показывает этот пример:
void f(int n, int x) {
Gadget *p = new Gadget{n};
if(x<100) throw SomeException{};
if(x<200) return;
delete p;
}
Это небезопасно в C ++. Но это также небезопасно в Java! В C ++, если функция возвращается рано, delete
никогда не будет вызываться. Но если у вас была полная сборка мусора, такая как в Java, вы просто получаете предположение, что объект будет уничтожен «в какой-то момент в будущем» ( Обновление: это еще хуже, чем это. Java делает НеОбещаю называть финализатор когда-либо - возможно, он никогда не будет вызван). Этого недостаточно, если гаджет содержит дескриптор открытого файла, или соединение с базой данных, или данные, которые вы буферизировали для записи в базу данных на более позднем этапе. Мы хотим, чтобы гаджет был уничтожен, как только он будет завершен, чтобы освободить эти ресурсы как можно скорее. Вы не хотите, чтобы ваш сервер баз данных боролся с тысячами соединений с базами данных, которые больше не нужны - он не знает, что ваша программа закончила работать.
Так в чем же решение? Есть несколько подходов. Очевидный подход, который вы будете использовать для подавляющего большинства ваших объектов:
void f(int n, int x) {
Gadget p = {n}; // Just leave it on the stack (where it belongs!)
if(x<100) throw SomeException{};
if(x<200) return;
}
Для этого требуется меньше символов. Это не new
мешает. Это не требует, чтобы вы печатали Gadget
дважды. Объект уничтожается в конце функции. Если это то, что вы хотите, это очень интуитивно понятно. Gadget
s ведут себя так же, как int
или double
. Предсказуемый, легкий для чтения, легкий в обучении. Все это «ценность». Иногда это большая ценность, но ценности легче учить, потому что у вас нет такого «действия на расстоянии», которое вы получаете с помощью указателей (или ссылок).
Большинство созданных вами объектов предназначены для использования только в функции, которая их создала, и, возможно, передаются в качестве входных данных для дочерних функций. Программисту не нужно думать о «управлении памятью» при возврате объектов или иным образом обмениваться объектами между различными частями программного обеспечения.
Область и срок службы важны. В большинстве случаев проще, если время жизни совпадает с областью действия. Это легче понять и легче учить. Когда вам нужно другое время жизни, должно быть очевидно, что вы читаете код, который делаете, shared_ptr
например. (Или возвращая (большие) объекты по значению, используя семантику перемещения или unique_ptr
.
Это может показаться проблемой эффективности. Что делать, если я хочу вернуть гаджет foo()
? Семантика перемещения C ++ 11 облегчает возвращение больших объектов. Просто напишите, Gadget foo() { ... }
и это будет просто работать, и работать быстро. Вам не нужно связываться с &&
самим собой, просто возвращайте вещи по значению, и язык часто сможет выполнить необходимые оптимизации. (Еще до C ++ 03 компиляторы проделали замечательную работу, избегая ненужного копирования.)
Как сказал Страуструп в другом месте на видео (перефразируя): «Только компьютерный специалист будет настаивать на копировании объекта, а затем уничтожении оригинала. (Аудитория смеется). Почему бы просто не переместить объект непосредственно в новое место? Это то, что люди (не компьютерные ученые) ожидать. "
Когда вы можете гарантировать, что требуется только одна копия объекта, гораздо проще понять время существования объекта. Вы можете выбрать, какую политику жизни вы хотите, и уборка мусора есть, если хотите. Но когда вы поймете преимущества других подходов, вы обнаружите, что сборка мусора находится внизу вашего списка предпочтений.
Если это не работает для вас, вы можете использовать unique_ptr
, или если это невозможно, shared_ptr
. Хорошо написанный C ++ 11 короче, его легче читать и легче изучать, чем многие другие языки, когда речь заходит об управлении памятью.