Menu
Coddy logo textTech

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

challenge icon

Задание

Легко

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

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор C++