7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RAIIとは

RAII (Resource Acquisition Is Initialization) は、C++のリソース管理の基本原則。

「リソースの取得をオブジェクトの初期化に、解放をデストラクタに任せる」

これを理解すると、C++のリソース管理がグッと楽になる。

// ❌ 手動管理(危険)
FILE* file = fopen("data.txt", "r");
// ... 例外が発生したら? → リソースリーク
fclose(file);

// ✓ RAII(安全)
{
    std::ifstream file("data.txt");
    // ... 例外が発生しても
}  // スコープを抜けると自動でclose

基本的な実装

class FileHandle {
private:
    FILE* file;
    
public:
    FileHandle(const char* filename, const char* mode) {
        file = fopen(filename, mode);
        if (!file) {
            throw std::runtime_error("Failed to open file");
        }
    }
    
    ~FileHandle() {
        if (file) {
            fclose(file);  // 確実に解放
        }
    }
    
    // コピー禁止
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    
    // ムーブは許可
    FileHandle(FileHandle&& other) noexcept : file(other.file) {
        other.file = nullptr;
    }
};

使用例:

{
    FileHandle file("data.txt", "w");
    file.write("Hello!");
}  // 自動でfclose

ロックガード

std::lock_guardはmutexのRAIIラッパーです。

std::mutex mtx;

void safe_increment() {
    std::lock_guard<std::mutex> lock(mtx);  // ロック取得
    ++counter;
}  // 自動でunlock

// ❌ 手動unlock(危険)
void dangerous_increment() {
    mtx.lock();
    ++counter;
    // ここで例外が発生すると永遠にロック!
    mtx.unlock();
}

各種ロックガード

クラス 用途
lock_guard 基本的なスコープロック
unique_lock 柔軟(遅延ロック、条件変数)
scoped_lock 複数mutexを安全にロック
// 複数mutexを安全にロック(デッドロック回避)
std::scoped_lock lock(mutex1, mutex2);

スマートポインタ

// unique_ptr: 単独所有
{
    auto ptr = std::make_unique<MyClass>();
}  // 自動でdelete

// shared_ptr: 共有所有
{
    auto ptr1 = std::make_shared<MyClass>();
    auto ptr2 = ptr1;  // 共有
}  // 参照カウントが0になったらdelete

スコープガード

任意のクリーンアップ処理をRAIIで実行:

template<typename F>
class ScopeGuard {
private:
    F cleanup;
    bool active;
    
public:
    explicit ScopeGuard(F f) : cleanup(std::move(f)), active(true) {}
    
    ~ScopeGuard() {
        if (active) cleanup();
    }
    
    void dismiss() { active = false; }  // キャンセル
    
    ScopeGuard(const ScopeGuard&) = delete;
};

template<typename F>
ScopeGuard<F> make_scope_guard(F f) {
    return ScopeGuard<F>(std::move(f));
}

使用例:

int* raw_ptr = new int(42);
auto guard = make_scope_guard([&]() {
    delete raw_ptr;
});

// 例外が発生してもguardが確実にdelete

トランザクション

成功時のみコミット、失敗時はロールバック:

class Transaction {
private:
    bool committed = false;
    std::vector<std::function<void()>> rollback_actions;
    
public:
    void add_rollback(std::function<void()> action) {
        rollback_actions.push_back(std::move(action));
    }
    
    void commit() { committed = true; }
    
    ~Transaction() {
        if (!committed) {
            // 逆順でロールバック
            for (auto it = rollback_actions.rbegin(); 
                 it != rollback_actions.rend(); ++it) {
                (*it)();
            }
        }
    }
};

使用例:

Transaction tx;

// 操作1
do_operation1();
tx.add_rollback([]() { undo_operation1(); });

// 操作2
do_operation2();
tx.add_rollback([]() { undo_operation2(); });

tx.commit();  // 成功ならコミット
// 例外でcommit()が呼ばれなければ自動ロールバック

標準ライブラリのRAII

クラス 管理するリソース
std::string 動的メモリ
std::vector 動的配列
std::fstream ファイル
std::unique_ptr 動的オブジェクト
std::shared_ptr 共有オブジェクト
std::lock_guard mutex
std::thread スレッド(joinable)

アンチパターン

❌ rawポインタでnew/delete

// 危険
int* p = new int[100];
// ... 例外が発生したら?
delete[] p;

// 安全
auto p = std::make_unique<int[]>(100);

❌ リソースをクラス外で管理

// 危険:解放忘れの可能性
FILE* open_file(const char* name);
void close_file(FILE* f);

// 安全:RAIIでラップ
class File {
    FILE* f;
public:
    File(const char* name) : f(fopen(name, "r")) {}
    ~File() { if (f) fclose(f); }
};

❌ 手動でのunlock

// 危険
mtx.lock();
do_something();  // 例外で永遠にロック
mtx.unlock();

// 安全
std::lock_guard<std::mutex> lock(mtx);
do_something();

RAIIの設計原則

  1. コンストラクタでリソース取得
  2. デストラクタでリソース解放
  3. コピーは禁止 or 深いコピー
  4. ムーブは許可(所有権移動)
  5. 例外安全性を確保
class Resource {
public:
    Resource() { /* 取得 */ }
    ~Resource() { /* 解放 */ }
    
    // コピー禁止
    Resource(const Resource&) = delete;
    Resource& operator=(const Resource&) = delete;
    
    // ムーブ許可
    Resource(Resource&&) noexcept;
    Resource& operator=(Resource&&) noexcept;
};

まとめ

リソース RAIIラッパー
メモリ unique_ptr, shared_ptr
ファイル fstream
mutex lock_guard, unique_lock
その他 カスタムクラス or スコープガード

C++でリソースを扱うなら、必ずRAIIを使いましょう。

// これだけ覚える
{
    auto resource = make_resource();
}  // 自動で解放
7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?