RAII в C++
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 16 из 104.
RAII (Resource Acquisition Is Initialization) — фундаментальный идиом C++, в котором управление resource связано со временем жизни object. Идея проста: получать resource в constructor и освобождать их в destructor.
Когда создаётся object, он получает все необходимые ему ресурсы. Когда object выходит из области видимости, его destructor автоматически освобождает эти ресурсы. Это гарантирует очистку, даже если возникают исключения.
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 получает владение памятью в куче в своём constructor и удаляет её в своём destructor. Ты уже использовал RAII, сам того не осознавая!
RAII применяется к любому resource: памяти, дескрипторам file, сетевым соединениям, мьютексам или соединениям с базой данных. Оборачивая resource в object, C++ обеспечивает детерминированную очистку и устраняет целые категории ошибок, такие как утечки и висячие дескрипторы.
Задание
ЛегкоДавайте создадим менеджер соединений, который демонстрирует RAII, автоматически управляя жизненным циклом сетевых соединений. Когда соединение создаётся, оно должно "open", а когда выходит из области видимости, оно должно автоматически "close": ручная очистка не требуется!
Вы создадите два файла, чтобы организовать свой код:
Connection.h: определите классConnection, который управляет ресурсом соединения, используя принципы RAII. У класса должны быть:- приватный атрибут
connectionId(строка) для идентификации соединения - приватный атрибут
isOpen(тип bool) для отслеживания состояния соединения - конструктор, который принимает идентификатор соединения, устанавливает
isOpenв значение true и выводит"Opening connection: <id>" - деструктор, который проверяет, открыто ли соединение, закрывает его, устанавливая
isOpenв значение false, и выводит"Closing connection: <id>" - метод
send(string message), который выводит"[<id>] Sending: <message>", если соединение открыто - метод
getId(), который возвращает идентификатор соединения
- приватный атрибут
main.cpp: продемонстрируйте, как RAII обеспечивает автоматическую очистку, считывая из ввода идентификатор соединения и сообщение. Затем:- создайте область видимости с помощью фигурных скобок
{ } - внутри этой области видимости создайте объект
Connectionв стеке, используя введённый идентификатор - используйте соединение для отправки введённого сообщения
- позвольте области видимости завершиться (соединение должно автоматически закрыться)
- после завершения области видимости выведите
"Connection out of scope"
- создайте область видимости с помощью фигурных скобок
Преимущество RAII заключается в том, что вам не нужно помнить о закрытии соединения: деструктор автоматически обрабатывает это, когда объект покидает область видимости. Этот шаблон предотвращает утечки ресурсов, даже если в вашем коде произойдёт что-то неожиданное.
Подключите заголовочный файл в main.cpp с помощью #include "Connection.h".
Попробуйте сами
#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: After the scope ends, print "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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++