Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие интерфейсы используют RAII
RAII (Resource Acquisition Is Initialization) — это идиома C++, где управление ресурсами привязано к жизненному циклу объекта. Ресурсы захватываются в конструкторе и освобождаются в деструкторе. Множество интерфейсов в C++ используют этот принцип.
1. Умные указатели (Smart Pointers)
unique_ptr
class File {
public:
File(const string& name) {
handle = open(name.c_str(), O_RDONLY);
if (handle < 0) throw runtime_error("Cannot open");
}
~File() {
if (handle >= 0) close(handle);
}
private:
int handle;
};
// RAII: файл закроется автоматически
{
unique_ptr<File> file = make_unique<File>("data.txt");
file->read();
} // Деструктор вызывается, файл закрывается
shared_ptr
class Database {
public:
Database(const string& connection_string) {
conn = dbConnect(connection_string);
}
~Database() {
if (conn) dbDisconnect(conn);
}
private:
DBConnection* conn;
};
// RAII с подсчётом ссылок
{
shared_ptr<Database> db1 = make_shared<Database>("host=localhost");
{
shared_ptr<Database> db2 = db1; // ref_count = 2
} // db2 уничтожена, ref_count = 1
} // db1 уничтожена, ref_count = 0, вызывается деструктор
2. Потоки и синхронизация
std::thread
void worker() {
cout << "Working..." << endl;
}
{
thread t(worker);
// ...делаем что-то...
} // ОШИБКА: не присоединился!
// Правильно с RAII:
{
thread t(worker);
// ...делаем что-то...
t.join(); // Присоединяемся
} // Деструктор не вызывает terminate()
std::lock_guard и std::unique_lock
class ThreadSafeCounter {
private:
mutable mutex mut;
int counter = 0;
public:
void increment() {
lock_guard<mutex> lock(mut); // Захват в конструкторе
counter++;
} // Освобождение в деструкторе
int value() const {
unique_lock<mutex> lock(mut);
return counter;
} // Освобождение в деструкторе
};
// Безопасна даже при исключении
void example() {
ThreadSafeCounter counter;
try {
counter.increment();
throw runtime_error("Ошибка!");
} catch (...) {
// Мьютекс уже освобождён благодаря RAII
}
}
3. Файловые потоки
std::ifstream и std::ofstream
{
ifstream input("data.txt");
if (!input) throw runtime_error("Cannot open");
string line;
while (getline(input, line)) {
cout << line << endl;
}
} // Файл закроется в деструкторе
// Безопасна при исключении
void readFile(const string& filename) {
ifstream file(filename);
if (!file.is_open()) {
throw runtime_error("Cannot open");
}
string line;
while (getline(file, line)) {
if (line.empty()) {
throw runtime_error("Empty line");
}
process(line);
}
} // Файл закроется даже при исключении
4. Контейнеры STL
std::vector, std::map, std::string
{
vector<int> vec;
vec.reserve(1000); // Выделение памяти
for (int i = 0; i < 100; i++) {
vec.push_back(i);
}
// Используем вектор
} // Вся память освобождается в деструкторе
// Безопасна при исключении
class DataProcessor {
private:
map<string, vector<int>> data;
public:
void process() {
data["key1"].push_back(1);
if (someCondition()) {
throw runtime_error("Error");
}
data["key2"].push_back(2);
}
} // Все контейнеры очищаются автоматически
5. Таймеры и async операции
std::async
future<int> calculateAsync() {
return async(launch::async, []() {
// Вычисления в отдельном потоке
return 42;
});
}
{
future<int> result = calculateAsync();
int value = result.get(); // Ждём результата
} // Поток завершится, ресурсы освобождены
6. Custom RAII классы
Пример: Connection pool
class Connection {
public:
Connection(ConnectionPool& pool, const string& addr)
: pool_(pool), handle_(connect(addr)) {
if (!handle_) throw runtime_error("Cannot connect");
}
~Connection() {
disconnect(handle_);
}
void execute(const string& query) {
if (!handle_) throw runtime_error("Not connected");
runQuery(handle_, query);
}
private:
ConnectionPool& pool_;
ConnectionHandle handle_;
};
// Использование
{
Connection conn(pool, "db.example.com");
conn.execute("SELECT * FROM users");
} // Соединение закроется, вернётся в пул
Пример: Локальное блокирование ресурса
class ResourceLock {
public:
ResourceLock(Resource& res) : resource_(res) {
resource_.lock(); // Захватываем
}
~ResourceLock() {
resource_.unlock(); // Отпускаем
}
private:
Resource& resource_;
};
void criticalSection(Resource& res) {
ResourceLock lock(res); // RAII: захват
// Работаем с res
} // RAII: освобождение
Пример: Временное изменение состояния
class State {
public:
bool debug = false;
class DebugMode {
public:
DebugMode(State& state) : state_(state), saved_(state.debug) {
state_.debug = true;
}
~DebugMode() {
state_.debug = saved_;
}
private:
State& state_;
bool saved_;
};
};
{
State state;
{
State::DebugMode debug(state);
// debug = true
}
// debug = false (восстановлено)
}
7. Транзакции в БД
class Transaction {
public:
Transaction(Database& db) : db_(db) {
db_.beginTransaction();
}
~Transaction() {
if (committed_) {
db_.commit();
} else {
db_.rollback();
}
}
void commit() {
committed_ = true;
}
private:
Database& db_;
bool committed_ = false;
};
// Безопасна при ошибках
{
Transaction tx(db);
try {
db.execute("INSERT INTO users VALUES (1, 'John')");
db.execute("INSERT INTO users VALUES (2, 'Jane')");
tx.commit();
} catch (...) {
// Автоматический rollback в деструкторе
}
}
Преимущества RAII
- Автоматическое управление: Нет утечек ресурсов
- Exception safety: Деструктор вызывается даже при исключении
- Простота: Разработчик не думает об освобождении
- Scope-based: Ресурсы следуют области видимости переменной
- Zero-cost abstraction: Компилятор оптимизирует код
Anti-patterns (чего избегать)
// ПЛОХО: нарушение RAII
{
FILE* file = fopen("data.txt", "r");
if (!file) throw runtime_error("Cannot open");
try {
readFile(file);
} catch (...) {
fclose(file); // Может не выполниться при вложенном исключении
throw;
}
fclose(file);
}
// ХОРОШО: RAII
{
unique_ptr<FILE, decltype(&fclose)> file(
fopen("data.txt", "r"),
&fclose
);
if (!file) throw runtime_error("Cannot open");
readFile(file.get());
} // Гарантированное закрытие
Заключение
RAII — фундаментальный принцип C++, используемый буквально везде: от умных указателей до STL контейнеров. Это гарантирует безопасное управление ресурсами и исключительную надёжность программ. Каждый раз, когда нужно управлять ресурсом, следует создать класс, который следует идиоме RAII.