本文へスキップ
BecomeCoder

C++コース · 第16章 メモリ管理とスマートポインタ ― 漏らさない所有権 · レッスン66

shared_ptr と weak_ptr ― 共有する所有権

ブラウザで完結

導入

1つの資源を「複数の場所で共有して持ちたい」ことがあります。そのためのスマートポインタが std::shared_ptr です。仕組み(参照カウント)と、その落とし穴を避ける weak_ptr まで押さえて、この章を締めます。

説明

shared_ptr は「何人が所有しているか」を数える参照カウントを持ちます。コピーするとカウントが増え、寿命が尽きると減り、0 になった瞬間に自動で解放されます。make_shared<型>(引数) で作ります。

flowchart TB
  r["資源(Dog)"]
  a["shared_ptr a"] --> r
  b["shared_ptr b(a をコピー)"] --> r
  note["参照カウント = 2<br/>両方が消えて 0 になったら解放"]
#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> a = make_shared<int>(42);
    cout << a.use_count() << endl;   // 1(所有者は1人)

    {
        shared_ptr<int> b = a;       // 共有(コピーOK)
        cout << a.use_count() << endl;   // 2
        cout << *b << endl;          // 42
    }   // ここで b が消え、カウントが 1 に戻る

    cout << a.use_count() << endl;   // 1
    return 0;   // a が消えてカウント 0 → ここで解放
}

注意点が「循環参照」です。2つのオブジェクトshared_ptr で互いを持ち合うと、カウントが永遠に 0 にならず解放されません。片方を 所有しない参照 weak_ptr にすると断ち切れます。

#include <memory>
using namespace std;

struct Node {
    shared_ptr<Node> next;   // 次を所有
    weak_ptr<Node> prev;     // 前は所有しない(weak)→ 循環を防ぐ
};
flowchart TB
  a["shared_ptr で相互所有<br/>→ カウントが 0 にならず漏れる"]
  b["片方を weak_ptr に<br/>→ 循環が切れて正しく解放"]
  a -->|"循環を断つ"| b

まとめ

shared_ptr は参照カウントで所有を共有し、カウントが 0 で自動解放します。共有が本当に必要なときだけ使い、既定は unique_ptr を選びます。相互に持ち合う「循環参照」は weak_ptr(所有しない参照)で断ち切ります。これで生の new/delete を書かずに、安全なメモリ管理ができます。

実際に動かしてみよう

下のエディタにC++を書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のC++コンパイラ(clang + libc++)がその場でコンパイルして実行し、出力が表示されます。std::string・vector・クラス・テンプレート・STL などモダンC++がそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(cin)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。