Идиома Resource Acquisition Is Initialization

Идиома Resource Acquisition Is Initialization

Получение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII)) — шаблон проектирования объектно-ориентированного программирования, смысл которого заключается в том, что получение некоторого ресурса совмещается с инициализацией, а освобождение — с уничтожением объекта.

Получение доступа к ресурсу происходит в конструкторе, а освобождение в деструкторе. Поскольку деструктор автоматической переменной вызывается при выходе из её области видимости, то ресурс гарантировано освобождается при уничтожении переменной. Это справедливо и в ситуациях, в которых возникают исключения. Это делает RAII ключевой концепцией для написания безопасного при исключениях кода.

Содержание

Применения

Эта концепция может использоваться для любых разделяемых объектов или ресурсов:

Важный случай использования RAII — «умные указатели»: классы, инкапсулирующие владение памятью. Например, в стандартной библиотеке шаблонов языка C++ для этой цели существует класс auto_ptr.

Пример

Пример класса на языке C++, реализующего захват ресурсов при инициализации:

#include <cstdio>
#include <stdexcept>
 
class file {
public:
    file( const char* filename ) : m_file_handle(std::fopen(filename, "w+")) {
        if( !m_file_handle )
            throw std::runtime_error("file open failure") ;
    }
    ~file() {
        if( std::fclose(m_file_handle) != 0 ) {
            // fclose() может вернуть ошибку при записи на диск последних изменений
        }
    }
 
    void write( const char* str ) {
        if( std::fputs(str, m_file_handle) == EOF )
            throw std::runtime_error("file write failure") ;
    }
 
private:
    std::FILE* m_file_handle ;
 
    // Копирование и присваивание не реализовано.  Предотвратим их использование,
    // объявив соответствующие методы закрытыми.
    file( const file & ) ;
    file & operator=( const file & ) ;
};
 
// пример использования этого класса
void example_usage() {
   // открываем файл (захватываем ресурс)
    file logfile("logfile.txt") ;
 
    logfile.write("hello logfile!") ;
 
    // продолжаем использовать logfile...
    // Можно возбуждать исключения или выходить из функции не беспокоясь о закрытии файла; 
    // он будет закрыт автоматически когда переменная logfile выйдет из области видимости.
}

Суть шаблона проектирования RAII в том, что класс инкапсулирует владение (захват и освобождение) некоторого ресурса, например, открытого файлового дескриптора. Когда объекты-экземпляры такого класса являются автоматическими переменными, гарантируется, что когда они выйдут из области видимости, будет вызван их деструктор, а значит ресурс будет освобождён. В данном примере, файл будет закрыт корректно даже если вызов fstat() вернёт ошибку и будет возбуждено исключение. Более того, если конструктор класса File завершился корректно, это гарантирует то, что файл действительно открыт. В случае ошибки при открытии файла, конструктор возбуждает исключение.

При помощи RAII и автоматических переменных можно просто управлять владением нескольких ресурсов. Порядок вызова деструкторов является обратным порядку вызова конструкторов; деструктор вызывается только если объект был полностью создан (то есть, если конструктор не возбудил исключение).

Использование шаблона проектирования RAII упрощает код и помогает обеспечить корректность работы программы.

Управление владением ресурсов без RAII

В

Тут бремя явного освобождения ресурсов возложено на программиста, в каждом месте кода, где выполняется захват ресурса.

Ruby и Smalltalk не поддерживают RAII, но в них существует похожий шаблон написания кода, который состоит в том, что методы передают ресурсы в блоки-замыкания. Вот пример на языке Ruby:

File.open("logfile.txt", "w+") do |logfile|
   logfile.write("hello logfile!")
end
# Метод 'open' гарантирует то, что файл будет закрыт без каких-либо
# явных действий со стороны кода, выполняющего запись в файл

Оператор 'with' языка using' языков С# и Visual Basic 2005 обеспечивают детерминированное управление владением ресурсов внутри блока и заменяют блок finally, приблизительно как и в языке Ruby.

В подсчёта ссылок, что позволяет реализовывать RAII так же, как и в C++: объекты, на которые не существует ссылок, немедленно удаляются и вызывается деструктор, который может освободить ресурс. Но, время жизни объектов не обязательно привязано в какой-то области видимости. Например, можно создать объект внутри функции, а потом присвоить ссылку на него некоторой глобальной переменной, тем самым увеличив время жизни объекта на неопределённое время (и оставив ресурс захваченным на это время). Это может являться причиной утечки ресурсов, которые должны были быть освобождены в момент выхода объекта из области видимости.

При написании кода на языке Си требуется больше кода, управляющего владением ресурсов, так как он не поддерживает исключений, блоков try-finally или других синтаксических конструкций, позволяющих реализовать RAII. Обычно, код пишут по следующей схеме: освобождение ресурсов выполняется в конце функции и в начале этого кода ставится метка; в середине функции, в случае возникновения ошибок, выполняется их обработка, а затем переход на освобождение ресурсов при помощи оператора goto. Таким образом, код освобождения ресурсов не дублируется в каждом месте обработки ошибки.

int c_example()
{
    int retval = 0; // возвращаем 0 в случае успешного завершения
    FILE *f = fopen("logfile.txt", "w+");
    if(!f)
    {
        retval = -1;
        goto just_return;
    }
    if( fputs("hello logfile!", f) == EOF ) {
        retval = -2;
        goto close_file;
    }
 
    // продолжаем использовать ресурс
 
    // Освобождаем ресурсы (в обратном порядке)
  close_file:
    if ( fclose(f) == EOF ) {
        retval = -3;
    }
 
  just_return:
    return retval;
}

Существуют незначительно отличающиеся способы написания такого кода, но задача этого примера — показать саму идею в общем случае.

См. также


Wikimedia Foundation. 2010.

Игры ⚽ Нужно решить контрольную?

Полезное


Смотреть что такое "Идиома Resource Acquisition Is Initialization" в других словарях:

  • Идиома copy-and-swap — Идиома copy and swap  это идиома языка программирования C++, позволяющая разрабатывать устойчивые к исключениям операторы присваивания. Идиома базируется на идиоме Resource Acquisition Is Initialization. Идиома предполагает реализацию… …   Википедия

  • Swap-идиома — Идиома copy and swap это идиома языка программирования C++, позволяющая разрабатывать устойчивые к исключениям операторы присваивания. Идиома базируется на идиоме Resource Acquisition Is Initialization. Идиома предполагает реализацию следующих… …   Википедия

  • Получение ресурса есть инициализация — (англ. Resource Acquisition Is Initialization (RAII))  программная идиома объектно ориентированного программирования, смысл которой заключается в том, что получение некоторого ресурса совмещается с инициализацией, а освобождение  с …   Википедия


Поделиться ссылкой на выделенное

Прямая ссылка:
Нажмите правой клавишей мыши и выберите «Копировать ссылку»