Получение ресурса есть инициализация — cppreference.com
Материал из cppreference.com
Получение ресурса есть инициализация или RAII, это техника программирования C++[1][2], которая связывает жизненный цикл ресурса, который должен быть получен перед использованием (выделенная память в куче, поток выполнения, открытый сокет, открытый файл, заблокированный мьютекс, дисковое пространство, подключение к базе данных, всё, что существует в ограниченном количестве) с временем жизни объекта.
RAII гарантирует, что ресурс доступен любой функции, которая может иметь доступ к объекту (доступность ресурса это инвариант класса, что устраняет избыточные тесты во время выполнения). Это также гарантирует, что все ресурсы высвобождаются, когда время жизни их управляющего объекта заканчивается, в порядке, обратном получению. Аналогичным образом, если получение ресурсов завершается с ошибкой (конструктор завершает работу с исключением), все ресурсы, полученные каждым полностью сконструированным элементом и базовым подобъектом, освобождаются в порядке, обратном инициализации. При этом используются основные функции языка (время жизни объекта, выход из области видимости, порядок инициализации и раскручивание стека), чтобы исключить утечку ресурсов и гарантировать безопасность исключений. По другому эта техника называется Управление Ресурсами С Привязкой К Области Видимости (SBRM - Scope-Bound Resource Management), по основному варианту использования, когда время жизни объекта RAII заканчивается из-за выхода из области видимости.
RAII можно резюмировать следующим образом:
- инкапсулировать каждый ресурс в класс, где
- конструктор получает ресурс и устанавливает все инварианты класса или генерирует исключение, если это невозможно сделать,
- деструктор освобождает ресурс и никогда не генерирует исключений;
- всегда использовать ресурс через экземпляр класса RAII, который либо
- имеет автоматическую длительность хранения или временное время жизни, или
- имеет время жизни, которое ограничено временем жизни автоматического или временного объекта.
|
Семантика перемещения позволяет безопасно передавать право собственности на ресурсы между объектами, между областями видимости, а также внутри и вне потоков, сохраняя при этом безопасность ресурсов. |
(начиная с C++11) |
Классы с open()/close(), lock()/unlock(), или init()/copyFrom()/destroy() методами являются типичным примером не-RAII классов.
std::mutex m; void bad() { m.lock(); // получает мьютекс f(); // если f() генерирует исключение, мьютекс никогда // не будет освобождён if(!everything_ok()) return; // мьютекс не освобждён при раннем выходе из функции m.unlock(); // если bad() достигает этого оператора мьютекс // будет освобождён } void good() { std::lock_guard<std::mutex> lk(m); // класс RAII: получение мьютекса есть инициализация f(); // если f() генерирует исключение, мьютекс // будет освобождён if(!everything_ok()) return; // мьютекс освобождён в случае раннего выхода // из функции } // если good() завершается корректно мьютекс // будет освобождён
Стандартная библиотека
Классы бибилиотеки C++, которые управляют собстенными ресурсами, следуют RAII: std::string, std::vector, std::jthread (начиная с C++20) и многие другие получают свои ресурсы в конструкторах (которые генерируют исключения при ошибках), освобождают их в деструкторах (никогда не генерирующих исключений) и не требуют явной очистки.
|
Кроме того, стандартная библиотека предлагает несколько RAII обёрток для управления предоставленными пользователями ресурсами:
|
(начиная с C++11) |
Примечание
RAII не применяется к управлению ресурсами, которые не были получены перед использованием: процессорное время, количество ядер и объём кэш-памяти, ёмкость энтропийного пула, пропускная способность сети, потребление электроэнергии, память стека.