RAII
Получение ресурса есть инициализация (Resource Acquisition Is Initialization), или RAII, — это приём программирования на C++[1][2], который связывает жизненный цикл ресурса, который необходимо получить до использования (выделенная память в куче, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс, дисковое пространство, подключение к базе данных — всё, что существует в ограниченном количестве), со временем жизни объекта.
RAII гарантирует, что ресурс доступен любой функции, которая может получить доступ к объекту (доступность ресурса — это инвариант класса, устраняющий необходимость в избыточных проверках во время выполнения). Кроме того, гарантируется, что все ресурсы будут освобождены по окончании времени жизни управляющего ими объекта в порядке, обратном порядку их получения. Аналогично, если получить ресурс не удаётся (конструктор завершается с исключением), все ресурсы, полученные каждым полностью сконструированным подобъектом-членом и базовым подобъектом, освобождаются в порядке, обратном порядку инициализации. Для устранения утечек ресурсов и обеспечения безопасности при исключениях используются основные возможности языка (время жизни объекта, выход из области видимости, порядок инициализации и раскрутка стека). Другое название этого приёма — управление ресурсами, привязанное к области видимости (SBRM), по основному случаю использования, когда время жизни объекта RAII завершается при выходе из области видимости.
Принцип RAII можно сформулировать следующим образом:
- инкапсулировать каждый ресурс в класс, где
- конструктор получает ресурс и устанавливает все инварианты класса либо выбрасывает исключение, если это невозможно,
- деструктор освобождает ресурс и никогда не выбрасывает исключений;
- всегда использовать ресурс через экземпляр класса RAII, который либо
- сам имеет автоматическую длительность хранения или временное время жизни, либо
- имеет время жизни, ограниченное временем жизни автоматического или временного объекта.
| Семантика перемещения позволяет передавать ресурсы и владение ими между объектами, в контейнеры и из них, а также между потоками, обеспечивая безопасность работы с ресурсами. | (начиная с C++11) |
К типичным примерам классов, не использующих RAII, относятся классы с функциями-членами open()/close(), lock()/unlock() или init()/copyFrom()/destroy():
std::mutex m;
void bad()
{
m.lock(); // acquire the mutex
f(); // if f() throws an exception, the mutex is never released
if (!everything_ok())
return; // early return, the mutex is never released
m.unlock(); // if bad() reaches this statement, the mutex is released
}
void good()
{
std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization
f(); // if f() throws an exception, the mutex is released
if (!everything_ok())
return; // early return, the mutex is released
} // if good() returns normally, the mutex is released
Стандартная библиотека
Классы библиотеки C++, самостоятельно управляющие своими ресурсами, следуют принципу RAII: std::string, std::vector, std::jthread(начиная с C++20) и многие другие получают ресурсы в конструкторах (которые выбрасывают исключения при ошибках), освобождают их в деструкторах (которые никогда не выбрасывают исключений) и не требуют явной очистки.
| Кроме того, стандартная библиотека предоставляет несколько обёрток RAII для управления ресурсами, предоставляемыми пользователем:
| (начиная с C++11) |
Примечания
RAII неприменим к управлению ресурсами, которые не получают до использования: процессорным временем, доступностью ядер, ёмкостью кэша, ёмкостью пула энтропии, пропускной способностью сети, потреблением электроэнергии и памятью стека. Для таких ресурсов конструктор класса C++ не может гарантировать доступность ресурса в течение всего времени жизни объекта, поэтому необходимо использовать другие средства управления ресурсами.
Внешние ссылки
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/raii