Stroustrup зробив кілька хороших коментарів до цього на конференції Going Native 2013.
Просто переходьте до приблизно 25 мільйонів у цьому відео . (Рекомендую переглянути все відео насправді, але це переходить до матеріалів про збирання сміття.)
Коли у вас є дійсно чудова мова, яка дозволяє легко (і безпечно, і передбачувано, і легко читати, і легко навчати) поводитися з предметами та цінностями прямо, уникаючи (явного) використання купи, тоді ви навіть не хочете збирати сміття.
З сучасним C ++ та речей, які ми маємо в C ++ 11, збирання сміття більше не бажане, за винятком обмежених обставин. Насправді, навіть якщо хороший сміттєзбірник вбудований в один із головних компіляторів С ++, я думаю, що він буде використовуватися не дуже часто. Уникнути ГК буде простіше , а не важче.
Він показує цей приклад:
void f(int n, int x) {
Gadget *p = new Gadget{n};
if(x<100) throw SomeException{};
if(x<200) return;
delete p;
}
Це небезпечно для C ++. Але це також небезпечно на Яві! У 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 коротший, легший для читання та простіший у навчанні, ніж багато інших мов, що стосується управління пам'яттю.