Динамічний розподіл потрібен лише тоді, коли час життя об'єкта повинен відрізнятися від обсягу, який він створює (Це справедливо і для того, щоб зменшити обсяг як більший), і у вас є конкретна причина, коли зберігання його за значенням не означає робота.
Наприклад:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
З C ++ 11 ми маємо std::unique_ptr
справу з виділеною пам'яттю, яка містить право власності на виділену пам'ять.std::shared_ptr
створено для того, коли вам належить поділитися правом власності. (вам знадобиться це менше, ніж ви очікували в хорошій програмі)
Створити примірник стає дуже просто:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
Також додається C ++ 17, std::optional
що може заважати вимагати виділення пам'яті
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Як тільки «екземпляр» виходить із сфери застосування, пам'ять очищається. Передача права власності також проста:
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
То коли ж вам ще потрібно new
? Майже ніколи з C ++ 11 о. Більшу частину ви використовуєте, std::make_unique
поки не доберетесь до точки, коли ви потрапили на API, який передає право власності через необроблені покажчики.
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
У програмі C ++ 98/03 ви повинні виконати керування пам'яттю вручну. Якщо ви в цьому випадку, спробуйте оновити до більш нової версії стандарту. Якщо ви застрягли:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Переконайтесь, що ви правильно відстежуєте право власності, щоб не було витоків пам'яті! Ще не працює семантика руху.
Отже, коли нам потрібен malloc в C ++? Єдиною вагомою причиною було б виділити пам'ять та ініціалізувати її пізніше, розміщуючи нову.
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
Незважаючи на те, що вищезазначене є дійсним, це можна зробити і через нового оператора. std::vector
є хорошим прикладом для цього.
Нарешті, у нас ще є слон в кімнаті: C
. Якщо вам доведеться працювати з бібліотекою С, де пам'ять отримується в коді C ++ і звільняється в коді С (або навпаки), ви змушені використовувати malloc / free.
Якщо ви в цьому випадку, забудьте про віртуальні функції, функції членів, класи ... Дозволені лише структури з POD-адресами в ньому.
Деякі винятки з правил:
- Ви пишете стандартну бібліотеку з розширеними структурами даних, де malloc є відповідним
- Вам потрібно виділити велику кількість пам'яті (в копію пам'яті файлу 10 ГБ?)
- У вас є інструменти, які заважають вам використовувати певні конструкції
- Вам потрібно зберігати неповний тип