← Назад к вопросам

Какие знаешь примеры представляющие RAII помимо умных указателей?

1.7 Middle🔥 161 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Примеры RAII помимо умных указателей

RAII (Resource Acquisition Is Initialization) — это один из самых мощных паттернов в C++, гарантирующий автоматическое управление ресурсами. Рассмотрим разнообразные практические примеры за пределами умных указателей.

1. Lock Guards для синхронизации потоков

Самый популярный пример после умных указателей:

#include <mutex>
#include <thread>

std::mutex mtx;

void safe_function() {
    std::lock_guard<std::mutex> lock(mtx);  // Захватываем мьютекс в конструкторе
    
    // Критическая секция
    std::cout << "Безопасный доступ\n";
    
} // Деструктор автоматически освобождает мьютекс

Более новый и универсальный вариант:

#include <shared_mutex>

std::shared_mutex rw_mtx;

void read_safe() {
    std::shared_lock<std::shared_mutex> lock(rw_mtx);  // Read lock
    // Несколько потоков могут читать одновременно
}

void write_safe() {
    std::unique_lock<std::shared_mutex> lock(rw_mtx);  // Write lock
    // Эксклюзивный доступ
    
    // Можно временно разблокировать
    lock.unlock();
    // ... какой-то код
    lock.lock();  // И снова заблокировать
}

Преимущество: Даже если выкинуть исключение, мьютекс будет освобождён.

2. File I/O RAII обёртки

Управление файлами — классический случай для RAII:

#include <fstream>

class FileHandler {
private:
    std::ofstream file;
    
public:
    FileHandler(const std::string& filename) {
        file.open(filename);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open file");
        }
        std::cout << "File opened: " << filename << "\n";
    }
    
    ~FileHandler() {
        if (file.is_open()) {
            file.close();
            std::cout << "File closed\n";
        }
    }
    
    void write(const std::string& data) {
        file << data;
    }
    
    // Запретить копирование
    FileHandler(const FileHandler&) = delete;
    FileHandler& operator=(const FileHandler&) = delete;
};

int main() {
    try {
        FileHandler fh("output.txt");
        fh.write("Hello, RAII!");
    } // Файл автоматически закроется здесь
    
    return 0;
}

Стандартная библиотека уже имеет RAII файлы:

{
    std::ifstream file("data.txt");  // Открытие в конструкторе
    std::string line;
    std::getline(file, line);
} // Закрытие в деструкторе

3. Database Connection Pool

Управление подключениями к БД:

#include <memory>

class DatabaseConnection {
private:
    std::string connection_string;
    bool is_connected = false;
    
public:
    DatabaseConnection(const std::string& cs) : connection_string(cs) {
        // Логирование и инициализация подключения
        std::cout << "Connecting to: " << cs << "\n";
        is_connected = true;
    }
    
    ~DatabaseConnection() {
        if (is_connected) {
            std::cout << "Disconnecting from database\n";
            is_connected = false;
        }
    }
    
    void execute(const std::string& query) {
        if (!is_connected) throw std::runtime_error("Not connected");
        std::cout << "Executing: " << query << "\n";
    }
    
    DatabaseConnection(const DatabaseConnection&) = delete;
    DatabaseConnection& operator=(const DatabaseConnection&) = delete;
};

class ConnectionGuard {
private:
    std::unique_ptr<DatabaseConnection> conn;
    
public:
    ConnectionGuard(const std::string& connection_string) 
        : conn(std::make_unique<DatabaseConnection>(connection_string)) {}
    
    DatabaseConnection* operator->() { return conn.get(); }
    DatabaseConnection& operator*() { return *conn; }
    
    // Деструктор автоматически удалит подключение
};

int main() {
    ConnectionGuard db("postgresql://localhost/mydb");
    db->execute("SELECT * FROM users");
} // Подключение автоматически закроется

4. Scope Guards для деинициализации

Подобно defer в Go:

#include <functional>

class ScopeGuard {
private:
    std::function<void()> exit_func;
    
public:
    ScopeGuard(std::function<void()> f) : exit_func(f) {}
    
    ~ScopeGuard() {
        if (exit_func) exit_func();
    }
    
    // Отменить выполнение при выходе
    void release() { exit_func = nullptr; }
    
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};

int main() {
    ScopeGuard cleanup([] {
        std::cout << "Cleanup resources\n";
    });
    
    std::cout << "Doing work\n";
} // Cleanup выполнится здесь

Практический пример:

void process_data() {
    void* memory = malloc(1024);
    
    ScopeGuard mem_cleanup([memory] {
        free(memory);
        std::cout << "Memory freed\n";
    });
    
    // Используем memory
    // Даже если выкинуть исключение, память освободится
} // Автоматическое освобождение памяти

5. Timer и Performance Monitoring

Подсчёт времени выполнения:

#include <chrono>

class Timer {
private:
    std::chrono::high_resolution_clock::time_point start;
    std::string operation_name;
    
public:
    Timer(const std::string& name) : operation_name(name) {
        start = std::chrono::high_resolution_clock::now();
        std::cout << "Timer started for: " << name << "\n";
    }
    
    ~Timer() {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
            end - start
        ).count();
        std::cout << operation_name << " took: " << duration << "ms\n";
    }
    
    Timer(const Timer&) = delete;
    Timer& operator=(const Timer&) = delete;
};

void expensive_operation() {
    Timer t("database_query");
    // Тяжёлая работа
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
} // Автоматический вывод времени выполнения

6. Memory Pool с автоматическое очисткой

class MemoryBlock {
private:
    char* data;
    size_t size;
    
public:
    MemoryBlock(size_t sz) : size(sz) {
        data = new char[sz];
        std::cout << "Allocated " << sz << " bytes\n";
    }
    
    ~MemoryBlock() {
        delete[] data;
        std::cout << "Deallocated " << size << " bytes\n";
    }
    
    char* get() { return data; }
    size_t get_size() const { return size; }
    
    MemoryBlock(const MemoryBlock&) = delete;
    MemoryBlock& operator=(const MemoryBlock&) = delete;
};

std::vector<MemoryBlock> pool;

void allocate_and_use() {
    pool.emplace_back(1024);  // Конструктор вызывается
    pool.emplace_back(2048);
    
    // Используем
    std::cout << "Using memory pools\n";
    
} // Деструкторы вызываются автоматически

7. Resource Wrapper для API

class APIClient {
private:
    void* api_handle;
    std::string api_key;
    
public:
    APIClient(const std::string& key) : api_key(key) {
        // Инициализация API в конструкторе
        std::cout << "API initialized with key: " << key.substr(0, 4) << "...\n";
        api_handle = reinterpret_cast<void*>(this);
    }
    
    ~APIClient() {
        // Очистка в деструкторе
        std::cout << "API client destroyed\n";
    }
    
    std::string make_request(const std::string& endpoint) {
        std::cout << "Making request to: " << endpoint << "\n";
        return "response_data";
    }
    
    APIClient(const APIClient&) = delete;
    APIClient& operator=(const APIClient&) = delete;
};

void fetch_user_data() {
    APIClient api("secret_key_12345");
    auto data = api.make_request("/users/123");
    std::cout << "Received: " << data << "\n";
} // Очистка API автоматически

Ключевые принципы RAII

ПринципОписание
КонструкторЗахватывает ресурс (файл, мьютекс, память)
ДеструкторГарантированно освобождает ресурс
ИсключенияРаботает даже при выбросе исключения
Scope-basedРесурс привязан к времени жизни объекта

Почему RAII лучше try-finally

// Плохо: что если выкинуть исключение между lock и unlock?
mtx.lock();
try {
    do_something();
    mtx.unlock();
} catch (...) {
    mtx.unlock();  // Легко забыть!
    throw;
}

// Хорошо: RAII гарантирует освобождение
{
    std::lock_guard<std::mutex> lock(mtx);
    do_something();
} // Деструктор ВСЕГДА вызывается

Итог: RAII — это не просто паттерн для умных указателей, это философия управления ресурсами в C++. Применяй RAII для файлов, мьютексов, таймеров, подключений и любых других ресурсов, которые требуют явной очистки.

Какие знаешь примеры представляющие RAII помимо умных указателей? | PrepBro