Menu
Coddy logo textTech

C++ における RAII

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 16/104。

RAII (Resource Acquisition Is Initialization) は、リソース管理をオブジェクトの生存期間(ライフタイム)に結び付ける、C++の基本的なイディオムです。考え方は単純です。コンストラクタでリソースを確保し、デストラクタでそれらを解放します。

オブジェクトが作成される際、それは必要とするあらゆるリソースを取得します。オブジェクトがスコープから外れると、そのデストラクタが自動的にそれらのリソースを解放します。これにより、例外が発生した場合でもクリーンアップが保証されます。

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は、コンストラクタでヒープメモリの所有権を取得し、デストラクタでそれを削除します。あなたはすでに、気づかないうちにRAIIを使用しています!

RAIIは、メモリ、ファイルハンドル、ネットワーク接続、ミューテックス、データベース接続など、あらゆるリソースに適用されます。リソースをオブジェクトでラップすることにより、C++は決定論的なクリーンアップを保証し、リークやダングリングハンドルのようなあらゆるカテゴリのバグを排除します。

challenge icon

チャレンジ

簡単

ネットワーク接続のようなリソースのライフサイクルを自動的に処理することで、RAIIを実証する接続マネージャーを構築しましょう。接続が作成されたときに「オープン」し、スコープ外に出たときに自動的に「クローズ」するようにします。手動でのクリーンアップは不要です!

コードを整理するために2つのファイルを作成します:

  • Connection.h: RAIIの原則を使用して接続リソースを管理する Connection クラスを定義します。クラスには以下を含める必要があります:
    • 接続を識別するためのプライベートな connectionId 属性 (string)
    • 接続状態を追跡するためのプライベートな isOpen 属性 (bool)
    • 接続IDを受け取り、isOpen を true に設定し、"Opening connection: <id>" と出力するコンストラクタ
    • 接続が開いているかを確認し、isOpen を false に設定して閉じ、"Closing connection: <id>" と出力するデストラクタ
    • 接続が開いている場合に "[<id>] Sending: <message>" と出力する send(string message) メソッド
    • 接続IDを返す getId() メソッド
  • main.cpp: 入力から接続IDとメッセージを読み取り、RAIIが自動クリーンアップをどのように保証するかを実証します。その後:
    • 波括弧 { } を使用してスコープを作成します
    • そのスコープ内で、入力されたIDを使用してスタック上に Connection オブジェクトを作成します
    • 接続を使用して入力メッセージを送信します
    • スコープを終了させます(接続は自動的に閉じられるはずです)
    • スコープの後に、"Connection out of scope" と出力します

RAIIの素晴らしさは、接続を閉じることを覚えておく必要がないことです。オブジェクトがスコープを抜けるときに、デストラクタが自動的に処理します。このパターンは、コード内で予期しないことが起こったとしても、リソースリークを防ぎます。

main.cpp で #include "Connection.h" を使用してヘッダーファイルをインクルードしてください。

チートシート

RAII (Resource Acquisition Is Initialization) は、リソース管理をオブジェクトの生存期間に結びつけるC++のイディオムです。リソースはコンストラクタで取得され、デストラクタで解放されます。

オブジェクトがスコープ外になると、そのデストラクタが自動的にリソースを解放し、例外が発生した場合でもクリーンアップを保証します:

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");  // リソースが取得される
    // ファイルを操作する...
    // 例外がスローされても、デストラクタは実行されます!
}   // リソースは自動的に解放されます

unique_ptr のようなスマートポインタはRAIIの例です。これらはコンストラクタでヒープメモリを確保し、デストラクタでそれを削除します。

RAIIは、メモリ、ファイルハンドル、ネットワーク接続、ミューテックス、データベース接続など、あらゆるリソースに適用されます。これにより決定論的なクリーンアップが保証され、リークやダングリングハンドルのようなバグが排除されます。

自分で試してみよう

#include <iostream>
#include <string>
#include "Connection.h"

using namespace std;

int main() {
    string connectionId;
    string message;
    
    // 入力を読み込む
    cin >> connectionId;
    cin.ignore();
    getline(cin, message);

    // TODO: 波括弧 { } を使用してスコープを作成する
    // スコープ内:
    //   - connectionId を使用してスタック上に Connection オブジェクトを作成する
    //   - 接続を使用してメッセージを送信する
    // スコープを終了させる(デストラクタが自動的に呼び出される)

    // TODO: スコープが終了した後、"Connection out of scope" と出力する

    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン