Страуструп сделал несколько хороших комментариев по этому поводу на конференции Going Native 2013 года.
Просто пропустите до 25m50s в этом видео . (Я бы порекомендовал посмотреть все видео на самом деле, но это пропускает материал о сборке мусора.)
Когда у вас действительно отличный язык, который позволяет легко (и безопасно, и предсказуемо, и легко читаемо, и легко научить) обращаться с объектами и значениями прямым способом, избегая (явного) использование кучи, тогда вы даже не хотите сборщик мусора.
С современным C ++ и тем, что есть в C ++ 11, сборка мусора больше не нужна, за исключением ограниченных обстоятельств. Фактически, даже если хороший сборщик мусора встроен в один из основных компиляторов C ++, я думаю, что он не будет использоваться очень часто. будет проще , а не сложнее, избегать GC.
Он показывает этот пример:
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 короче, его легче читать и легче изучать, чем многие другие языки, когда речь заходит об управлении памятью.