Що означає "Придбання ресурсів" - ініціалізація (RAII)?
Що означає "Придбання ресурсів" - ініціалізація (RAII)?
Відповіді:
Це дійсно жахлива назва неймовірно потужної концепції, і, можливо, одна з речей № 1, якої розробники C ++ пропускають, переходячи на інші мови. Дещо було зроблено рух, щоб спробувати перейменувати цю концепцію як Управління ресурсами , пов’язані з обсягами , хоча, здається, це ще не наздогнало.
Коли ми говоримо "Ресурс", ми не маємо на увазі лише пам'ять - це можуть бути файлові ручки, мережеві сокети, ручки бази даних, об'єкти GDI ... Коротше кажучи, речі, які у нас є обмеженими, і тому нам потрібно вміти контролювати їх використання. Аспект 'пов'язаний зі сферою дії' означає, що термін експлуатації об'єкта пов'язаний з областю змінної, тому коли змінна виходить із сфери дії, деструктор вивільнить ресурс. Дуже корисною властивістю цього є те, що це сприяє підвищенню безпеки винятків. Наприклад, порівняйте це:
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
З RAII
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
В останньому випадку, коли виняток викидається і стек розкручується, локальні змінні знищуються, що забезпечує очищення нашого ресурсу і не протікає.
Scope-Bound
найкращий вибір імені тут, оскільки специфікатори класу зберігання разом із областю визначають тривалість зберігання об'єкта. Звуження, зроблене для обмеження сфери, може бути корисним спрощенням, однак воно не на 100% точне
Це ідіома програмування, яка коротко означає, що ви
Це гарантує, що все, що трапиться під час використання ресурсу, з часом вивільняється (через нормальне повернення, знищення об'єкта, що міститься, або викид, який викидається).
Це широко застосовувана добра практика в C ++, оскільки, крім того, що це безпечний спосіб поводження з ресурсами, він також робить ваш код набагато чистішим, оскільки вам не потрібно змішувати код обробки помилок з основним функціоналом.
*
Оновлення: "локальний" може означати локальну змінну або нестатичну змінну члена класу. В останньому випадку змінна-член ініціалізується та знищується об'єктом власника.
**
Update2: як зазначає @sbi, ресурс - хоча часто виділяється всередині конструктора - може також виділятися зовні і передаватися як параметр.
open()
/ close()
методів для ініціалізації та випуску ресурсу, лише конструктор та деструктор, тому "утримання" ресурсу - це лише час експлуатації об'єкта, незалежно від того, чи буде це життя обробляється контекстом (стек) або явно (динамічний аллок)
"RAII" означає "Придбання ресурсів - це ініціалізація" і насправді є досить помилковим, оскільки це стосується не придбання ресурсів (і ініціалізації об'єкта), а вивільнення ресурсу (шляхом знищення об'єкта. ).
Але RAII - це ім'я, яке ми отримали, і воно дотримується.
В самому серці ідіома є інкапсуляція ресурсів (шматки пам'яті, відкриті файли, розблоковані файли, ви називаєте її) в локальних, автоматичних об'єктах , і деструктор цього об'єкта випускає ресурс при знищенні об'єкта кінець сфери, якій він належить:
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
Звичайно, об'єкти не завжди є локальними, автоматичними об'єктами. Вони також можуть бути членами класу:
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
Якщо такі об’єкти управляють пам'яттю, їх часто називають «розумними покажчиками».
Існує багато варіацій цього. Наприклад, у фрагментах першого коду виникає питання, що буде, якби хтось захотів скопіювати obj
. Найпростішим виходом було б просто заборонити копіювання. std::unique_ptr<>
, це робить розумний покажчик, що є частиною стандартної бібліотеки, як показано наступним стандартом C ++.
Ще один такий розумний вказівник, який std::shared_ptr
має "спільне володіння" ресурсом (динамічно виділеним об'єктом), який він містить. Тобто його можна вільно копіювати, і всі копії стосуються одного і того ж об’єкта. Смарт-покажчик відстежує, скільки копій посилається на один і той же об’єкт, і видалить його, коли остання знищена.
Третій варіант представленийstd::auto_ptr
який реалізує своєрідну семантику переміщення: Об'єкт належить лише одному вказівнику, а спроба скопіювати об’єкт призведе до (через синтаксичний хакер) передачі права власності на об'єкт цілі операції копіювання.
std::auto_ptr
застаріла версія std::unique_ptr
. std::auto_ptr
вид імітації семантики переміщення настільки, наскільки це було можливо в C ++ 98, std::unique_ptr
використовує нову семантику переміщення C ++ 11. Новий клас був створений , тому що переміщення семантика C ++ 11 є більш явною (потрібно std::move
тільки від тимчасового) в той час як він був по замовчуванням для будь-якої копії з неконстантних в std::auto_ptr
.
Термін експлуатації об'єкта визначається його обсягом. Однак іноді нам потрібно або корисно створити об’єкт, який живе незалежно від сфери, де він був створений. У C ++ оператор new
використовується для створення такого об’єкта. А для знищення об’єкта delete
може бути використаний оператор . Об'єкти, створені оператором new
, динамічно розподіляються, тобто розподіляються в динамічній пам'яті (також називається купою або вільним сховищем ). Отже, об’єкт, який було створено компанією, new
буде існувати, поки явно не буде знищено за допомогою delete
.
Деякі помилки, які можуть виникнути при використанні, new
і delete
це:
new
для виділення об'єкта та забуття на delete
об'єкт.delete
об'єкт, а потім використовуйте інший покажчик.delete
об'єкта двічі.Як правило, переважні масштабні змінні. Однак RAII можна використовувати як альтернативу new
та delete
зробити об’єкт живим незалежно від його сфери. Така методика полягає у підведенні вказівника на об’єкт, який був виділений на купі, та розміщення його в об'єкті ручки / менеджера . Останній має деструктор, який подбає про знищення об’єкта. Це гарантуватиме, що об'єкт буде доступний будь-якій функції, яка хоче отримати доступ до нього, і що об'єкт буде знищений, коли закінчується термін експлуатації об'єкта обробки , без необхідності явного очищення.
Прикладами зі стандартної бібліотеки C ++, які використовують RAII, є std::string
і std::vector
.
Розглянемо цей фрагмент коду:
void fn(const std::string& str)
{
std::vector<char> vec;
for (auto c : str)
vec.push_back(c);
// do something
}
коли ви створюєте вектор і натискаєте на нього елементи, вам не байдуже виділяти та розміщувати такі елементи. Вектор використовує new
для виділення місця для елементів на купі та delete
звільнення цього простору. Ви як користувач векторів не переймаєтесь деталями реалізації та довіряєте вектору не протікати. У цьому випадку вектор є руковим об'єктом його елементів.
Інші приклади зі стандартної бібліотеки , які використовують RAII є std::shared_ptr
, std::unique_ptr
і std::lock_guard
.
Інша назва цієї методики - SBRM , скорочення для управління ресурсами .
Книга Програмування на C ++ з розкритими шаблонами дизайну описує RAII як:
Де
Ресурси реалізуються у вигляді класів, а всі вказівники мають класові обгортки навколо них (що робить їх розумними покажчиками).
Ресурси набуваються шляхом виклику їх конструкторів, а вивільнення неявно (у зворотному порядку придбання) шляхом виклику їхніх деструкторів.
До класу RAII є три частини:
RAII означає "Придбання ресурсів - ініціалізація." Частина RAII "придбання ресурсів" - це те, коли ви починаєте щось, що має бути закінчено пізніше, наприклад:
Частина "є ініціалізацією" означає, що придбання відбувається всередині конструктора класу.
Ручне управління пам’яттю - це кошмар, який програмісти винайшли способи уникнути з часу створення компілятора. Мови програмування зі збирачами сміття полегшують життя, але ціною виконання. У цій статті - Усунення сміттєзбірника : шлях RAII , інженер Toptal Пітер Гудспід-Ніклаус дає нам заглянути до історії збирачів сміття та пояснює, як поняття власності та запозичень можуть допомогти усунути сміттєзбірники без шкоди їхнім гарантіям безпеки.