Що таке RAII? Приклади?


19

Завжди, коли використовується термін RAII, люди насправді говорять про деконструкцію замість ініціалізації. Я думаю, що я розумію, що це може означати, але я не зовсім впевнений. Також: чи є С ++ єдиною мовою RAII? Що з Java або C # /. NET?

Відповіді:


25

Придбання ресурсів - це ініціалізація означає, що об’єкти повинні доглядати за собою як повний пакет, а не очікувати, що інший код вкаже на примірник "ей, до речі, ви збираєтеся очиститись незабаром - будь ласка, приберіть зараз". Зазвичай це означає, що в деструкторі є щось значиме. Це також означає, що ви пишете клас спеціально для управління ресурсами, знаючи, що за певних важко передбачуваних обставин, як, наприклад, викидів винятків, ви можете розраховувати на виконання деструкторів.

Скажімо, ви хочете написати якийсь код, де ви збираєтеся змінити курсор Windows на очікуючий (пісочний годинник, пончик непрацюючого тощо), зробіть свої речі, а потім змініть його назад. І скажіть також, що "робити свої речі" може бути винятком. Таким способом RAII було б зробити клас, у якого ctor встановив курсор на очікування, чий один "реальний" метод зробив все, що ви хотіли, і чий dtor повернув курсор назад. Ресурси (в даному випадку стан курсору) прив’язані до області об’єкта. При придбанні ресурсу ви ініціалізуєте об'єкт. Ви можете розраховувати на те, що об’єкт буде знищений, якщо будуть викинуті винятки, а це означає, що ви можете розраховувати на очищення ресурсу.

Використання RAII добре означає, що вам не потрібно finally. Звичайно, він покладається на детерміновані руйнування, яких у Java не може бути. Ви можете отримати детерміновану деструкцію в C # і VB.NET за допомогою using.


4
Я думаю, що саме до цього ви стикаєтесь, але ви можете додати, що причина, чому Java та C # не підтримують RAII, пов’язана з збирачем сміття. У C ++ локальний об’єкт буде знищений, як тільки він вийде за межі області. У Java / C # це неправда.
Джейсон Бейкер

Розширюючись на пункт Ясона, причина, чому Java і C # не можуть гарантувати своєчасне знищення, полягає в можливості еталонних циклів, а це означає, що неможливо визначити безпечний порядок запуску деструкторів. Референтні цикли можуть траплятися і в C ++, але наслідки різні - програміст несе відповідальність за визначення порядку знищення та виконання явних видалень. Ця відповідальність дуже часто зберігається в деструкторі вищих класів вищого рівня - наприклад, клас контейнерів відповідає за те, щоб усі елементи, що містяться, були знищені. "Власність" є ключовою.
Steve314

1
@Jason це я мав на увазі під "детермінованим знищенням" - програміст C ++ знає, коли запуститься деструктор.
Кейт Григорій

Я знаю, що це стара відповідь, але я все ще дещо розгублений. Я щойно дізнався про термін, і деякі відомості говорять про те, що придбання має відбутися у конструкторі. Це насправді не має сенсу для мене, і ця відповідь, здається, суперечить їй, але ви могли б уточнити?
Пер Йоханссон

1
@PerJohansson Так, ви купуєте в ctor. І ви відпускаєте в дтор. Я зосереджувався на другому пункті, але вони йдуть разом. Після завершення роботи ctor ви знаєте, що у вас є дійсний об'єкт. І ви знаєте, що незалежно від того, що трапиться, ресурс буде випущений у потрібний час.
Кейт Григорій

4

RAII частково полягає у вирішенні питання про те, коли об’єкт несе відповідальність за власне очищення - правило полягає в тому, що об'єкт несе відповідальність, якщо і коли завершиться його ініціалізація конструктора. Симетрія ініціалізації та очищення, конструктор і деструктор, означає, що вони мають тісні зв’язки один з одним.

Одним із пунктів RAII є забезпечення безпеки виключень - що програма залишається самопослідовною, коли викидаються винятки. На перший погляд це тривіально - коли виняток змушує вийти з області застосування, локальні змінні в цій області потребують знищення.

Але що станеться, якщо викид виключення відбувається в конструкторі?

Ну, об’єкт не повністю побудований, тому його не можна безпечно знищити. Конструктор повинен мати спробні блоки, щоб забезпечити виконання будь-яких необхідних очищень, перш ніж виняток буде запропоновано. Після того, як виняток подає заявку за межі області, де був побудований об'єкт, виклику деструктора не буде, тому що об'єкт не був побудований в першу чергу.

Розглянемо, зокрема, конструктори даних членів усередині об'єкта, що руйнується. Якщо хтось із них кидає виняток, ваш основний код конструктора взагалі не працюватиме, але матиме якийсь код, який утворює неявну частину цього конструктора. Будь-які члени, які були успішно побудовані, будуть автоматично знищені. Будь-які члени, які не були побудовані (включаючи того, хто кинув виняток), не є.

Таким чином, RAII - це політика, яка забезпечує те, що все, що буде повністю сконструйоване, буде своєчасно знищено, особливо за наявності викидів винятків, і що будь-який об’єкт або повністю побудований, або його немає (немає половини- побудовані об’єкти, які ви не можете знати, як безпечно очистити). Ресурси, які виділяються, також звільняються. І багато роботи автоматизовано, тому програмісту не доведеться надто турбуватися про це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.