RAII в C++
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 16 из 104.
RAII (Resource Acquisition Is Initialization) — это фундаментальная идиома C++, в которой управление ресурсами привязано к времени жизни объекта. Идея проста: захватывайте ресурсы в конструкторе и освобождайте их в деструкторе.
Когда объект создается, он получает все необходимые ему ресурсы. Когда объект выходит из области видимости, его деструктор автоматически освобождает эти ресурсы. Это гарантирует очистку, даже если возникают исключения.
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // Захват ресурса
}
~FileHandler() {
if (file) std::fclose(file); // Освобождение ресурса
}
};С RAII вы никогда не забудете об очистке ресурсов:
void processFile() {
FileHandler handler("data.txt"); // Ресурс захвачен
// Работа с файлом...
// Если здесь возникнет исключение, деструктор все равно сработает!
} // Ресурс автоматически освобождаетсяУмные указатели — это идеальный пример RAII в действии. unique_ptr получает владение памятью в куче в своем конструкторе и удаляет ее в своем деструкторе. Вы уже использовали RAII, даже не осознавая этого!
RAII применим к любому ресурсу: памяти, дескрипторам файлов, сетевым соединениям, мьютексам или соединениям с базами данных. Оборачивая ресурсы в объекты, C++ обеспечивает детерминированную очистку и устраняет целые категории ошибок, таких как утечки и висячие дескрипторы.
Задание
ЛегкоДавайте создадим менеджер соединений, который демонстрирует RAII, автоматически управляя жизненным циклом сетевых соединений. Когда соединение создается, оно должно «открываться», а когда оно выходит из области видимости, оно должно автоматически «закрываться» — ручная очистка не требуется!
Вы создадите два файла для организации вашего кода:
Connection.h: Определите классConnection, который управляет ресурсом соединения, используя принципы RAII. Класс должен иметь:- Приватный атрибут
connectionId(string) для идентификации соединения - Приватный атрибут
isOpen(bool) для отслеживания состояния соединения - Конструктор, который принимает ID соединения, устанавливает
isOpenв true и выводит"Opening connection: <id>" - Деструктор, который проверяет, открыто ли соединение, закрывает его, устанавливая
isOpenв false, и выводит"Closing connection: <id>" - Метод
send(string message), который выводит"[<id>] Sending: <message>", если соединение открыто - Метод
getId(), который возвращает ID соединения
- Приватный атрибут
main.cpp: Продемонстрируйте, как RAII обеспечивает автоматическую очистку, считывая ID соединения и сообщение из входных данных. Затем:- Создайте область видимости, используя фигурные скобки
{ } - Внутри этой области видимости создайте объект
Connectionв стеке с введенным ID - Используйте соединение для отправки введенного сообщения
- Позвольте области видимости завершиться (соединение должно закрыться автоматически)
- После завершения области видимости выведите
"Connection out of scope"
- Создайте область видимости, используя фигурные скобки
Прелесть RAII заключается в том, что вам не нужно помнить о закрытии соединения — деструктор обрабатывает это автоматически, когда объект покидает область видимости. Этот паттерн предотвращает утечки ресурсов, даже если в вашем коде произойдет что-то неожиданное.
Включите ваш заголовочный файл в main.cpp с помощью #include "Connection.h".
Шпаргалка
RAII (Resource Acquisition Is Initialization — «получение ресурса есть инициализация») — это идиома C++, в которой управление ресурсами привязано к времени жизни объекта. Ресурсы захватываются в конструкторе и освобождаются в деструкторе.
Когда объект выходит из области видимости, его деструктор автоматически освобождает ресурсы, гарантируя очистку даже в случае возникновения исключений:
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // Захват ресурса
}
~FileHandler() {
if (file) std::fclose(file); // Освобождение ресурса
}
};RAII обеспечивает автоматическую очистку:
void processFile() {
FileHandler handler("data.txt"); // Ресурс захвачен
// Работа с файлом...
// Если возникнет исключение, деструктор все равно сработает!
} // Ресурс автоматически освобожденУмные указатели, такие как unique_ptr, являются примерами RAII. Они выделяют память в куче в конструкторе и удаляют её в деструкторе.
RAII применим к любому ресурсу: памяти, дескрипторам файлов, сетевым соединениям, мьютексам или подключениям к базам данных. Он обеспечивает детерминированную очистку и устраняет такие ошибки, как утечки и висячие указатели.
Попробуйте сами
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
string connectionId;
string message;
// Чтение входных данных
cin >> connectionId;
cin.ignore();
getline(cin, message);
// TODO: Создайте область видимости, используя фигурные скобки { }
// Внутри области видимости:
// - Создайте объект Connection в стеке с connectionId
// - Используйте соединение для отправки сообщения
// Позвольте области видимости завершиться (деструктор будет вызван автоматически)
// TODO: После завершения области видимости выведите "Connection out of scope"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер