Menu
Coddy logo textTech

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++ обеспечивает детерминированную очистку и устраняет целые категории ошибок, таких как утечки и висячие дескрипторы.

challenge icon

Задание

Легко

Давайте создадим менеджер соединений, который демонстрирует 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование