C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++のRAIIとスマートポインタ徹底解説!リソース管理を自動化するテクニック

C++におけるRAIIとは?

RAII(Resource Acquisition Is Initialization:リソース取得は初期化)は、C++においてリソースのライフサイクルを制御するための重要なプログラミング技法です。この手法では、リソースの寿命をオブジェクトの寿命に結び付けます。

具体的には、複数のリソースを1つのクラスにカプセル化し、オブジェクト生成時にコンストラクタでリソースを確保し、オブジェクト破棄時にデストラクタでリソースを解放します。

これにより、リソースはオブジェクトが生存している間は確実に保持され、スコープを抜けると自動的に解放されるため、メモリリークやリソースリークを防ぐことができます。また、例外が発生した場合でもデストラクタは確実に呼び出されるため、例外安全性の向上にも役立ちます。

RAIIのコード例

以下は、ファイル書き込み処理にRAIIを適用した例です。lock_guardofstreamは、いずれもRAIIの考え方に基づいた標準ライブラリのクラスです。

void file_write(const string& text) {
    static mutex m; // ファイルアクセスを保護するミューテックス
    lock_guard<mutex> lock(m); // ファイルへアクセスする前にロックを取得
    ofstream file("a.txt");
    if (!file.is_open()) { // ファイルが開けなかった場合
        throw runtime_error("unable to open file");
    }
    // テキストをファイルに書き込む
    file << text << std::endl;
}

この例では、lock_guardがコンストラクタでミューテックスをロックし、関数を抜ける際にデストラクタが自動的にアンロックします。ofstreamも同様に、オブジェクトが破棄されるときにファイルが自動的にクローズされます。

スマートポインタとは?

スマートポインタとは、通常のポインタを抽象化したデータ型であり、メモリ管理やファイル操作、ネットワークソケットなどのリソース管理を自動的に行えるようにするものです。自動的な破棄(delete)や参照カウントなど、さまざまな機能を実現できます。

C++では、スマートポインタは*演算子(参照外し)と->演算子(アロー演算子)をオーバーロードしたクラステンプレートとして実装できます。標準ライブラリが提供する主なスマートポインタには、以下のような種類があります。

  • auto_ptr:初期のスマートポインタですが、コピー時の挙動に問題があるため、C++11以降は非推奨となりました。
  • unique_ptr:単一の所有権を持つスマートポインタ。コピーはできず、所有権の移動のみが可能です。
  • shared_ptr:参照カウント方式により複数のポインタが同一オブジェクトを共有でき、最後の所有者が破棄されたときにリソースを解放します。
  • weak_ptrshared_ptrの循環参照を防ぐために使用される、所有権を持たない弱い参照です。

スマートポインタの自作例

ここでは、テンプレートクラスを使ってシンプルなスマートポインタを自作する例を紹介します。

#include <iostream>
using namespace std;

// 汎用スマートポインタクラス
template <class T>
class Smartpointer {
    T *p; // 実際のポインタ
public:
    // コンストラクタ
    Smartpointer(T *ptr = NULL) {
        p = ptr;
    }
    // デストラクタ
    ~Smartpointer() {
        delete(p);
    }
    // 参照外し演算子(*)のオーバーロード
    T & operator * () {
        return *p;
    }
    // アロー演算子(->)のオーバーロード
    // Tのメンバにポインタのようにアクセスできる
    T * operator -> () {
        return p;
    }
};

int main() {
    Smartpointer<int> p(new int());
    *p = 26;
    cout << "Value is: " << *p;
    return 0;
}

実行結果

Value is: 26

このように、自作のスマートポインタを使用すれば、main関数が終了してオブジェクトpが破棄されるタイミングで、デストラクタが自動的にdeleteを呼び出します。その結果、メモリ解放忘れによるリークを防ぎ、安全で保守性の高いコードを実現できます。

  1. C++で0・1・2のみの配列をソートする方法|DNF(オランダ国旗)アルゴリズムを解説

    0、1、2 のみで構成された配列が与えられたとき、すべての「0」を先頭に、「1」をその次に、「2」を末尾に配置するように要素を並べ替えることを考えます。このとき、追加のメモリを使用せずに配列をインプレース(in-place)でソートする必要があります。この問題は、DNF(Dutch National Flag:オランダ国旗)ソートアルゴリズムを使うことで効率的に解くことができます。入出力の例例1入力:arr[ ] = {2, 0, 0, 1, 2, 1}出力:0 0 1 1 2 2説明: DNFソートアルゴリズムを用いて0・1・2を含む配列を並べ替えると、{0, 0, 1, 1, 2, 2}

  2. Linuxのファイルグロブとは?ワイルドカードによるパス名展開を徹底解説

    ファイルグロブ(パス名展開)とは ファイルグロブ(File Globbing)は「パス名展開(Path Name Expansion)」とも呼ばれる仕組みで、Linuxにおいてワイルドカードのパターンを認識し、そのパターンに基づいてファイルパスを展開することで、条件に合致するファイルを見つける方法です。 ワイルドカードパターンとは ワイルドカードパターンとは、パターンをもとに複数のファイルを選択するために用いられる特殊な文字列のことです。「*」「?」「[]」といった文字を使用することで、柔軟なパターンマッチングやファイルの一括選択が可能になります。 アスタリスク(*):0文字以上の任意の文