Resource Acquisition Is Initialization

Ця стаття містить правописні, лексичні, граматичні, стилістичні або інші мовні помилки, які треба виправити. Ви можете допомогти вдосконалити цю статтю, погодивши її із чинними мовними стандартами.

Resource Acquisition Is Initialization (RAII), перекладається як «Отримання ресурсу є ініціалізація» — програмна ідіома, яка використовується в деяких об`єктно-орієнтовних мовах програмування, більшою мірою в C++, звідки вона взяла початок, але також застосовується в D, Ada, і Vala.

Відповідно RAII, утримання ресурсу тісно прив'язане до життєвого циклу об'єкта: алокація ресурсу (отримання) відбувається в момент створення об'єкта за допомогою конструктора, а деаллокація (звільнення) відбувається в момент знищення об'єкта за допомогою деструктора. Якщо об'єкти видаляються правильно, витоків ресурсів не відбувається.

Інші назви цієї ідіоми: Constructor Acquires, Destructor Releases (CADRe)[1] і Scope-based Resource Management (SBRM);[2]

Переваги

Перевагами RAII, як техніки керування ресурсами є те, що вона забезпечує інкапсуляцію, безпечність при виняткових ситуаціях (для відслідковування ресурсів), і локальність (дозволяє написати логіку отримання ресурсу і вивільнення поруч в одному місці).

Інкапсуляція забезпечується тим, що логіка управління ресурсами описана один раз в класі, а не в кожному місці де вона використовується. Безпека при виняткових ситуаціях забезпечується для стекових ресурсів (ресурси звільняються в тій самій області видимості в якій вони були створені) завдяки прив'язці до життєвого циклу стекової змінної (локальної змінної оголошеної в даному блоці): якщо виникає виняткова ситуація (англ. exception), і існує відповідний блок відловлювання помилок, єдиний код, який буде завжди виконаний після виходу із даної області видимості, це деструктори об'єктів об'явлених в цій області видимості. Локальність, забезпечується завдяки написанню конструктора і деструктора у визначенні класу поруч один з одним.

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

Типове застосування

RAII часто використовується для контролювання замикання м'ютексу (mutex) у багатопотокових застосуваннях. В такому випадку, при знищенні об'єкт гарантовано відпускає м'ютекс. Без застосування RAII існує потенційна загроза взаємного блокування (deadlock) м'ютексів. При використанні RAII, в код, який бере м'ютекс включає в собі логіку в якій м'ютекс буде звільнено коли виконання програми вийде за область видимості об'єкта.

Інше типове застосування, це робота з файлами: ми можемо мати об'єкт, який зберігає посилання на файл, який буде відкритий для запису в конструкторі, і закритий в деструкторі. В обох випадках, RAII гарантує лише те, що ресурс буде звільнено коректно, але програмісту слід контролювати безпеку при виникненні виняткових ситуацій власноруч. Якщо код, який проводить модифікацію даних, не є безпечним, м'ютекс може бути не взятий, або файл закритий так, що структура даних файлу буде пошкоджена.

Керування динамічно виділеними об'єктами також можна здійснювати за допомогою RAII. Для цього стандартна бібліотека C++ 11 має реалізацію так званого розумного вказівника std::unique_ptr для об'єкта з одним власником, і std::shared_ptr для об'єктів, на які існує декілька посилань. Схожі реалізації також існують в C++ 98 — std::auto_ptr і в бібліотеці Boost — boost::shared_ptr.

Приклад на C++11

Наступний приклад на C++11 демонструє використання RAII для доступу до файлів і синхронізації м'ютексів:

#include <string>
#include <mutex>
#include <iostream>
#include <fstream>
#include <stdexcept>

void write_to_file (const std::string & message) {
    // м’ютекс для захисту доступу для файлу
    static std::mutex mutex;

    // взяття м’ютексу перед операціями з файлом
    std::lock_guard<std::mutex> lock(mutex);

    // перевірка відкриття файлу
    std::ofstream file("example.txt");
    if (!file.is_open())
        throw std::runtime_error("Не можливо відкрити файл");
    
    // запис повідомлення до файлу
    file << message << std::endl;
    
    // файл буде закритий першим при покиданні області видимості (незалежно від виняткових ситуацій)
    // м’ютекс буде звільнений другим (в деструкторі lock)
}

Цей код є безпечним для виняткових ситуацій, оскільки C++ гарантує, що всі стекові об'єкти будуть знищенні після покидання області видимості. Деструктор для обох об'єктів: lock і file гарантовано буде викликаний при поверненні управління із функції, не залежно від того сталася виняткова ситуація (exception) чи ні.[3]

Локальні змінні дозволяють легко керувати кількома ресурсами в одній функції: вони знищуються у зворотньому порядку відповідно до того, як створювалися, а об'єкти знищуються лише тоді, коли вони були вдало створені, якщо не відбулося виняткових ситуацій в момент створення об'єкта.[4]

Використання RAII значно спрощує процедуру управління ресурсами, зменшує об'єми коду і дозволяє пересвідчитися в коректності програми, тому більша частина стандартної бібліотеки C++ дотримується цієї ідіоми.[5]

Примітки

  1. Архівована копія. Архів оригіналу за 4 липня 2014. Процитовано 11 липня 2014.{{cite web}}: Обслуговування CS1: Сторінки з текстом «archived copy» як значення параметру title (посилання)
  2. Masterminds of Programming: Conversations with the Creators of Major Programming Languages, с. 4, на «Google Books», O'Reilly Media, Inc., 21 бер. 2009–496 стор.
  3. dtors-shouldnt-throw. Архів оригіналу за 17 лютого 2013. Процитовано 12 лютого 2013.
  4. What's the order that local objects are destructed?. Архів оригіналу за 9 жовтня 2014. Процитовано 12 лютого 2013.
  5. too-many-trycatch-blocks. Архів оригіналу за 11 травня 2014. Процитовано 12 лютого 2013.


П:  Портал «Програмування»

PROG Це незавершена стаття про програмування.
Ви можете допомогти проєкту, виправивши або дописавши її.