RAII パターン

Resource Acquisition Is Initialization。リソース管理の基本。

概要

RAII(Resource Acquisition Is Initialization)は C++の最重要パターンです。

リソースの取得 = 初期化スコープ離脱 = リソース自動破棄

RAII の基本

#include <iostream>

class Resource {
public:
    Resource() {
        std::cout << "リソース取得" << std::endl;
    }
    
    ~Resource() {
        std::cout << "リソース解放" << std::endl;
    }
};

int main() {
    {
        Resource res;  // コンストラクタ: リソース取得
    }  // デストラクタ: リソース自動解放
    
    std::cout << "完了" << std::endl;
    return 0;
}

リソース取得
リソース解放
完了

スコープを抜けれ、自動的にデストラクタが呼ばれます。

ファイル操作での RAII

#include <iostream>
#include <fstream>

void writeFile() {
    std::ofstream file("data.txt");  // ファイルを開く(コンストラクタ)
    
    if (file) {
        file << "Hello, World!" << std::endl;
        file << "C++ RAII" << std::endl;
    }
}  // ファイルが自動的に閉じられる(デストラクタ)

int main() {
    writeFile();
    std::cout << "ファイル書き込み完了。自動で閉じられました。" << std::endl;
    return 0;
}

ファイル書き込み完了。自動で閉じられました。

手動でclose()を呼ぶ必要がありません。

例外安全性

RAII の大きな利点は例外安全性です。

#include <iostream>
#include <fstream>

void dangerousCode() {
    FILE* file = fopen("data.txt", "w");
    
    fprintf(file, "Data");
    
    if (/* エラー */) {
        throw std::runtime_error("エラー");
        // fclose(file); へ到達しない! メモリリーク!
    }
    
    fclose(file);
}

void safeCode() {
    std::ofstream file("data.txt");  // RAII で管理
    
    file << "Data";
    
    if (/* エラー */) {
        throw std::runtime_error("エラー");
        // スコープを抜ける際、自動的に閉じられる。リークしない!
    }
}

カスタムリソース管理クラス

#include <iostream>

class Database {
private:
    char* connection;
    
public:
    Database(const char* name) {
        connection = new char[256];
        snprintf(connection, 256, "DB: %s", name);
        std::cout << "DB接続: " << connection << std::endl;
    }
    
    ~Database() {
        std::cout << "DB切断: " << connection << std::endl;
        delete[] connection;
    }
    
    void query(const char* sql) {
        std::cout << "実行: " << sql << std::endl;
    }
};

int main() {
    {
        Database db("mydb");
        db.query("SELECT * FROM users");
    }  // 自動的に接続が切られる
    
    std::cout << "完了" << std::endl;
    return 0;
}

DB接続: DB: mydb
実行: SELECT * FROM users
DB切断: DB: mydb
完了

スマートポインタも RAII

#include <iostream>
#include <memory>

class Object {
public:
    ~Object() {
        std::cout << "Object 破棄" << std::endl;
    }
};

int main() {
    {
        std::unique_ptr<Object> obj = std::make_unique<Object>();
        std::cout << "Object 使用中" << std::endl;
    }  // スコープ離脱時に自動破棄
    
    std::cout << "完了" << std::endl;
    return 0;
}

Object 使用中
Object 破棄
完了

std::unique_ptrは RAII を実装しており、 スコープを抜ける際に自動削除されます。

ポイント

  • RAII: リソース取得 = 初期化
  • スコープ離脱時に自動破棄
  • 例外安全性の向上
  • スマートポインタは RAII の実装

まとめ

  • RAII は C++の基本設計パターン
  • 手動リソース管理を避ける
  • スマートポインタとカスタムクラスで実装