Raii c что это
Объекты классов могут на протяжении всего своего существования использовать различные ресурсы — динамически выделенная память, файлы, сетевые подключения и т.д. В этом случае в C++ применяется так называемый принцип/идиома RAII (resource acquisition is initialization). RAII предполагает, что получение ресурса производится при инициализации объекта. А освобождение ресурса производится в деструкторе объекта.
#include class IntArray < public: IntArray(unsigned size) : data< new int[size] ><> // выделяем память ~IntArray() < if(data) < std::cout > // Удаляем конструктор копирования и оператор присваивания IntArray(const IntArray&) = delete; IntArray& operator=(const IntArray&) = delete; // оператор индексирования для доступа к элементам int& operator[](unsigned index) < return data[index]; >// возвращаем инкапсулированный ресурс int* get() const < return data; >// передаем ресурс другогому объекту int* release() < int* result = data; data = nullptr; return result; >private: int* data; >; int main() < const unsigned count ; // количество элементов IntArray values; // создаем объект, который управляет ресурсом // изменяем элементы динамического массива for (unsigned i <>; i < count; ++i) < values[i] = i; >// выводим элементы динамического массива на консоль for (unsigned i <>; i < count; ++i) < std::cout std::cout
Здесь определен класс IntArray, который условно представляет массив чисел int и который управляет некоторым ресурсом. В данном случае ресурс представляет динамическую память, выделенную для хранения массива чисел int. Получение динамической памяти происходит в конструкторе объекта, а освобождение в деструкторе.
IntArray(unsigned size) : data < new int[size] ><> // выделяем память ~IntArray() < std::cout
Стоит отметить, что динамическая память представляет частный случая ресурсов (в реальности это могут быть файлы, сетевые подключения и т.д.) и тут используется прежде всего в целях демонстрации, поскольку вместо выделения-освобождения памяти в подобной ситуации мы можем использовать smart-указатели.
При этом важно, чтобы ресурс (в данном случае динамическая память) освобождался только один раз. Для этой цели в классе удалены конструктор копирования и оператор присваивания, что позволяет избежать ситуации, когда два объекта хранят указатель на одну и ту же область динамической памяти и соответственно потом в деструкторе будут пытаться освободить эту память.
IntArray(const IntArray&) = delete; IntArray& operator=(const IntArray&) = delete;
Для обращения к элементам динамического массива определен оператор индексирования [] , а для получения непосредственно указателя - функция get.
Стоит отметить функцию release, которая позволяет передать указатель на управления во вне, в том числе другой объект. В этом случае сбрасывае указатель в nullptr, а обязанность освободить память ляжет на внешней код, который получает этот указатель:
int* release()
В функции main создаем один объект IntArray:
int main() < const unsigned count ; // количество элементов IntArray values; // создаем объект, который управляет ресурсом
В итоге в конструкторе выделяется память, а после завершения функции main у IntArray вызывается деструктор, который освобождает память. Консольный вывод программы:
0 1 2 3 4 Freeing memory.
Посмотрим на применение функции release() :
int main() < const unsigned count ; // количество элементов IntArray array; // создаем объект, который управляет ресурсом // изменяем элементы динамического массива for (unsigned i <>; i < count; ++i) < array[i] = i; >// получаем указатель int* data = array.release(); // теперь функция main обязана освободить память for (unsigned i <>; i < count; ++i) < std::cout std::cout
Здесь указатель на динамического массива через функцию release передается в переменную data . После этого функция main несет ответственность за освобождение памяти, что и происходит в конце функции.
Как реализован RAII в разных языках
RAII (Resource Acquisition Is Initialization) — это техника программирования, которая связывает жизненный цикл ресурса, который должен быть получен перед использованием (выделенная память, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс и т. д.), с продолжительностью жизни объекта. RAII гарантирует, что ресурс доступен для любой функции, которая может получить доступ к объекту, и что все ресурсы освобождаются, когда заканчивается срок действия их управляющего объекта, в обратном порядке приобретения.
RAII является одной из основных концепций языка C++, которая позволяет эффективно управлять ресурсами с помощью конструкторов и деструкторов классов. В C++ RAII часто реализуется с помощью умных указателей, таких как std::unique_ptr или boost::shared_ptr, которые автоматически освобождают ресурс при выходе из области видимости.
Интересные факты и фичи языков программирования у нас в канале, заходи: )
В других языках программирования RAII может быть реализован по-разному или вообще отсутствовать.
- В Java RAII не поддерживается на уровне языка, так как нет деструкторов и нет гарантии вызова метода finalize. Вместо этого используется конструкция try-with-resources, которая позволяет автоматически закрывать ресурсы, реализующие интерфейс AutoCloseable.
- В Python RAII также не поддерживается на уровне языка, но есть возможность использовать менеджеры контекста с помощью оператора with. Менеджер контекста — это объект, который имеет методы enter и exit, которые вызываются при входе и выходе из блока with соответственно.
- В Rust RAII поддерживается на уровне языка и является ключевой частью системы безопасности памяти. В Rust все переменные имеют владельца (owner), который отвечает за освобождение ресурса при выходе из области видимости. Также есть возможность передавать владение (ownership) или заимствовать (borrow) переменные с помощью ссылок.
Что то интересное про RAII в С++(и не только)
RAII — это программная идиома, которая связывает жизненный цикл ресурса, который должен быть получен перед использованием (выделенная память, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс и т.д.), с временем жизни объекта.
RAII гарантирует, что ресурс доступен для любой функции, которая может обращаться к объекту, и что все ресурсы освобождаются, когда время жизни управляющего объекта заканчивается.
Вот некоторые интересные факты о RAII в С++:
- Идиома RAII была придумана Бьярне Страуструпом, создателем языка С++, в 1984 году. Он назвал ее «захват ресурса есть инициализация» (resource acquisition is initialization), хотя сам признал, что это неудачное название. Альтернативные названия — «конструктор получает, деструктор освобождает» (constructor acquires, destructor releases) или «управление ресурсами с привязкой к области видимости» (scope-bound resource management).
- Идиома RAII позволяет избежать утечек памяти, ошибок обращения к файлам и других проблем с ресурсами. Она также упрощает работу с исключениями, так как деструкторы вызываются автоматически при выходе из блока try-catch.
- Идиома RAII реализуется в С++ с помощью конструкторов и деструкторов классов. Конструктор отвечает за получение ресурса и его связывание с объектом. Деструктор отвечает за освобождение ресурса при уничтожении объекта.
- Идиома RAII лежит в основе так называемых «умных указателей» — классов std::unique_ptr и std::shared_ptr из стандартной библиотеки С++. Умные указатели оборачивают обычные указатели на динамически выделенную память и автоматически освобождают ее при выходе из области видимости.
- Идиома RAII также применяется в других языках программирования, таких как D, Ada и Vala. Однако не все языки поддерживают деструкторы или гарантируют их вызов при выходе из области видимости. Например, в Java и C# используется сборка мусора для автоматического освобождения памяти, но она не может гарантировать освобождение других ресурсов.
Получение ресурса есть инициализация
Получение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII) ) — программная идиома объектно-ориентированного программирования, смысл которой заключается в том, что получение некоторого ресурса совмещается с инициализацией, а освобождение — с уничтожением объекта.
Получение доступа к ресурсу происходит в конструкторе, а освобождение в деструкторе. Поскольку деструктор автоматической переменной вызывается при выходе её из области видимости, то ресурс гарантированно освобождается при уничтожении переменной. Это справедливо и в ситуациях, в которых возникают исключения. Это делает RAII ключевой концепцией для написания безопасного при исключениях кода.
Применения
Эта концепция может использоваться для любых разделяемых объектов или ресурсов:
- для выделения памяти,
- для открытия файлов или устройств,
- для мьютексов или критических секций и т. д.
Важный случай использования RAII — «умные указатели»: классы, инкапсулирующие владение памятью. Например, в стандартной библиотеке шаблонов языка C++ для этой цели существует класс auto_ptr (заменён на unique_ptr в C++11).
Пример
Пример класса на языке C++, реализующего захват ресурсов при инициализации:
#include #include 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 в том, что класс инкапсулирует владение (захват и освобождение) некоторого ресурса — например, открытого файлового дескриптора. Когда объекты-экземпляры такого класса являются автоматическими переменными, гарантируется, что когда они выйдут из области видимости, будет вызван их деструктор — а значит, ресурс будет освобождён. В данном примере файл будет закрыт корректно, даже если вызов std::fopen() вернёт ошибку и будет возбуждено исключение. Более того, если конструктор класса file завершился корректно, это гарантирует то, что файл действительно открыт. В случае ошибки при открытии файла конструктор возбуждает исключение.
При помощи RAII и автоматических переменных можно просто управлять владением нескольких ресурсов. Порядок вызова деструкторов является обратным порядку вызова конструкторов; деструктор вызывается только если объект был полностью создан (то есть если конструктор не возбудил исключение).
Использование RAII упрощает код и помогает обеспечить корректность работы программы.
Возможна реализация без исключений (например, это необходимо во встраиваемых приложениях). В этом случае используется конструктор по умолчанию, обнуляющий file handler, а для открытия файла используется отдельный метод типа bool FileOpen(const char *) . Смысл использования класса сохраняется, особенно если точек выхода из метода, где создаётся объект класса, несколько. Естественно, в этом случае в деструкторе проверяется необходимость закрытия файла.
Управление владением ресурсов без RAII
В Java, использующей сборку мусора, объекты, на которые ссылаются автоматические переменные, создаются при выполнении команды new, а удаляются сборщиком мусора, который запускается автоматически с неопределённой периодичностью. В Java нет деструкторов, которые будут гарантированно вызваны при выходе переменной из области видимости, а имеющиеся в языке финализаторы для освобождения ресурсов, кроме памяти, не подходят, так как неизвестно, когда объект будет удалён и будет ли удалён вообще. Поэтому об освобождении ресурсов программист должен заботиться сам. Предыдущий пример на Java можно переписать так:
void java_example() // открываем файл (захватываем ресурс) final LogFile logfile = new LogFile("logfile.txt") ; try logfile.write("hello logfile!") ; // продолжаем использовать logfile. // Можно возбуждать исключения, не беспокоясь о закрытии файла. // Файл будет закрыт при выполнении блока finally, который // гарантированно выполняется после блока try даже в случае // возникновения исключений. > finally // явно освобождаем ресурс logfile.close(); > >
Тут бремя явного освобождения ресурсов возложено на программиста, в каждом месте кода, где выполняется захват ресурса. В качестве синтаксического сахара в Java 7 введена конструкция «try-with-resources»:
void java_example() // открываем файл (захватываем ресурс) в заголовке конструкции try. // переменная logfile существует только внутри этого блока. try (LogFile logfile = new LogFile("logfile.txt")) logfile.write("hello logfile!") ; // продолжаем использовать logfile. > // здесь автоматически произойдёт вызов logfile.close(), независимо от // возникновения исключений в блоке кода. >
Чтобы этот код работал, класс LogFile должен реализовывать системный интерфейс java.lang.AutoCloseable и объявлять метод void close(); . Данная конструкция является, по сути, аналогом конструкции using()<> языка C#, также выполняющей инициализацию автоматической переменной объектом и гарантированный вызов метода освобождения ресурсов при выходе этой переменной из области видимости.
Ruby и Smalltalk не поддерживают RAII, но в них существует похожий шаблон написания кода, который состоит в том, что методы передают ресурсы в блоки-замыкания. Вот пример на языке Ruby:
File.open("logfile.txt", "w+") do |logfile| logfile.write("hello logfile!") end # Метод 'open' гарантирует то, что файл будет закрыт без каких-либо # явных действий со стороны кода, выполняющего запись в файл
Оператор ' with ' языка Python, оператор ' using ' языков С# и Visual Basic 2005 обеспечивают детерминированное управление владением ресурсов внутри блока и заменяют блок finally , приблизительно как и в языке Ruby.
В Perl время жизни объектов определяется при помощи подсчёта ссылок, что позволяет реализовывать 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; >
Существуют незначительно отличающиеся способы написания такого кода, но задача этого примера — показать саму идею в общем случае.
Псевдокод на python
Выразить идею raii на python можно так:
#coding: utf-8 resource_for_grep = False class RAII: g = globals() def __init__(self): self.g['resource_for_grep'] = True def __del__(self): self.g['resource_for_grep'] = False print resource_for_grep #False r = RAII() print resource_for_grep #True del r print resource_for_grep #False
См. также
- Найти и оформить в виде сносок ссылки на авторитетные источники, подтверждающие написанное.
- Проверить достоверность указанной в статье информации.
- Концепции языков программирования
- Объектно-ориентированное программирование
Wikimedia Foundation . 2010 .
Полезное
Смотреть что такое "Получение ресурса есть инициализация" в других словарях:
- Идиома Resource Acquisition Is Initialization — Получение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII)) шаблон проектирования объектно ориентированного программирования, смысл которого заключается в том, что получение некоторого ресурса совмещается с… … Википедия
- Шаблон проектирования — У этого термина существуют и другие значения, см. Паттерн. В разработке программного обеспечения, шаблон проектирования или паттерн (англ. design pattern) повторимая архитектурная конструкция, представляющая собой решение проблемы… … Википедия
- Идиома (программирование) — Для улучшения этой статьи по информационным технологиям желательно?: Дополнить статью (статья слишком короткая либо содержит лишь словарное определение). Найти и оформить в виде сносок ссылки на … Википедия
- Утечка памяти — (англ. memory leak) процесс неконтролируемого уменьшения объёма свободной оперативной памяти (RAM) компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками системных служб … Википедия
- Утечки памяти — Утечка памяти (англ. memory leak) процесс неконтролируемого уменьшения объёма свободной оперативной памяти (RAM) компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками… … Википедия
- Оптимизация (информатика) — Эта статья об оптимизации программ и данных вообще; об оптимизациях, применяемых компиляторами см.: Оптимизация компилятора. У этого термина существуют и другие значения, см. Оптимизация. Оптимизация модификация системы для улучшения её… … Википедия
- Обратная связь: Техподдержка, Реклама на сайте
- Путешествия
Экспорт словарей на сайты, сделанные на PHP,
WordPress, MODx.
- Пометить текст и поделитьсяИскать в этом же словареИскать синонимы
- Искать во всех словарях
- Искать в переводах
- Искать в ИнтернетеИскать в этой же категории