【C++】std::weak_ptr の使い方 | オブジェクトの生存を監視する方法

目次

はじめに

C++のstd::shared_ptrは、複数のポインタで一つのオブジェクトの所有権を共有できる、非常に便利なスマートポインタです。しかし、時には「オブジェクトの所有権は持ちたくないが、そのオブジェクトがまだ存在しているかどうかだけを知り、もし存在するなら安全にアクセスしたい」という場面があります。

この「所有権なき監視」を実現するのが std::weak_ptr です。weak_ptrは、shared_ptrによって管理されているオブジェクトを指しますが、所有者(参照カウント)の数には含まれません。

この記事では、weak_ptrの基本的な使い方と、それがどのようにして「ダングリングポインタ(無効なメモリを指すポインタ)」の問題を解決するのかを解説します。


【前提】C++11とは?

C++11は、2011年に正式化されたC++言語のメジャーアップデート版です。shared_ptrweak_ptrといったスマートポインタはこのC++11で導入されたため、利用するにはC++11以降に対応したコンパイラが必要です。


weak_ptr を使ったサンプルコード

このコードは、shared_ptrがスコープの内外で生存・消滅する様子を、weak_ptrを使って監視します。

完成コード

#include <iostream>
#include <memory> // shared_ptr, weak_ptr
#include <string>

using namespace std;

class Widget {
public:
    string name;
    Widget(string n) : name(n) { cout << "Widget「" << name << "」が生成されました。" << endl; }
    ~Widget() { cout << "Widget「" << name << "」が破棄されました。" << endl; }
};

int main() {
    // 1. weak_ptrを準備
    weak_ptr<Widget> weak_observer;
    
    cout << "--- ブロックに入ります ---" << endl;
    {
        // 2. shared_ptrでオブジェクトを生成
        shared_ptr<Widget> strong_owner = make_shared<Widget>("データA");
        
        // 3. weak_ptrにshared_ptrを代入して、監視を開始
        weak_observer = strong_owner;
        
        // 4. lock()で、監視対象が生きているか確認し、安全にアクセス
        if (shared_ptr<Widget> temp_strong_ptr = weak_observer.lock()) {
            cout << "ブロック内: オブジェクトは生きています。名前: " << temp_strong_ptr->name << endl;
        }
    } // このブロックを抜けるとき、strong_ownerが破棄され、Widgetオブジェクトも消滅する
    cout << "--- ブロックを出ました ---" << endl;

    // 5. ブロックを抜けた後、再度監視対象の状態を確認
    if (shared_ptr<Widget> temp_strong_ptr = weak_observer.lock()) {
        // このブロックは実行されない
        cout << "ブロック外: オブジェクトは生きています。" << endl;
    } else {
        cout << "ブロック外: オブジェクトは既に破棄されています。" << endl;
    }

    return 0;
}

コードの解説

1. weak_ptr<Widget> weak_observer;

Widgetオブジェクトを監視するためのweak_ptrを宣言します。この時点では何も指していません。

2. shared_ptr<Widget> strong_owner = ...

shared_ptrを使って、Widgetオブジェクトを動的に生成します。strong_ownerが、このオブジェクトの唯一の所有者です。

3. weak_observer = strong_owner;

weak_ptrshared_ptrを代入します。これにより、weak_observerstrong_ownerが管理するオブジェクトを「監視」するようになりますが、参照カウントは増加しません

4. if (shared_ptr<Widget> p = weak_observer.lock())

これがweak_ptrの最も重要な使い方です。

  • .lock(): weak_ptrが指しているオブジェクトがまだ存在していれば、そのオブジェクトを所有する新しいshared_ptrを返します。オブジェクトが既に破棄されていれば、空のshared_ptrnullptr相当)を返します。
  • if (shared_ptr<Widget> p = ...): lock()が返すshared_ptrif文の条件式内で宣言・初期化しています。
    • オブジェクトが存在する場合: pは有効なshared_ptrとなり、ifの条件はtrueと評価されます。このifブロック内では、pを通じてオブジェクトに安全にアクセスできます。
    • オブジェクトが破棄されている場合: pは空のshared_ptrとなり、ifの条件はfalseと評価されます。elseブロックが実行されます。

5. スコープとオブジェクトの消滅

{}ブロックを抜けると、strong_ownerはそのスコープの終わりで破棄されます。他に所有者がいないため、strong_ownerが管理していたWidgetオブジェクトもこのタイミングで破棄され、デストラクタが呼ばれます。

その後、ブロックの外で再度weak_observer.lock()を呼ぶと、監視対象はもう存在しないため、空のshared_ptrが返されます。


まとめ

今回は、C++11のstd::weak_ptrを使って、オブジェクトの生存を安全に監視する方法を解説しました。

  • **std::weak_ptr**は、shared_ptrが管理するオブジェクトを、所有権を持たずに参照する。
  • .lock()メソッドを呼び出すことで、監視対象のオブジェクトがまだ存在するかを確認し、存在すれば安全にアクセスするためのshared_ptrを取得できる。
  • これにより、ダングリングポインタ(無効なメモリを指すポインタ)の問題を回避できる。

weak_ptrは、オブジェクト同士が互いをshared_ptrで指し合ってメモリが解放されなくなる「循環参照」を解決するためにも不可欠なツールです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次