【C++】スマートポインタを使って簡単メモリ管理

C++でプログラマを悩ませるメモリ管理。

通常は、newで確保したメモリはdeleteで手動で解放する必要があります。

そこで登場するのが、「C++ スマートポインタ」。

スマートポインタを使用すると、不要になった時に自動的にメモリを解放してくれるため、メモリリークを防ぐことができます。

C++ スマートポインタ概要

C++のスマートポインタは、メモリ管理を自動化するためのクラスで、C++11から標準ライブラリに導入されました。

これにより、手動でのメモリ解放が不要になり、メモリリークやメモリの二重解放といった問題を防ぎ、安全で効率的なプログラムが実現できます。

C++ スマートポインタ

  • std::unique_ptr:1つの所有権のみを持つ
  • std::shared_ptr:複数のポインタでリソースを共有できる
  • std::weak_ptr:循環参照を防ぐために使用される

メモリリークについて

メモリリークとは、使い終わったメモリを解放しないことで、無駄にメモリが占領され続ける問題です。

たとえば、ノートに書いたメモを消さずにどんどん書き続けると、ノートがいっぱいになってしまい、新しいことが書けなくなります。

コンピューターでも同様に、メモリがいっぱいになると新しい処理ができなくなってしまいます。

メモリの二重解放について

メモリの二重解放とは、一度解放したメモリをもう一度解放しようとするミスです。

たとえば、友達に貸したノートを既に返してもらったのに、「もう一度返して」と言うようなものです。嫌われそうですね。

コンピューターでは、メモリが二重に解放されるとエラーが発生し、動作が不安定になってしまいます。

std::unique_ptrについて

std::unique_ptrは、所有権を唯一保持するスマートポインタです。

std::unique_ptrの特徴①:唯一の所有権

std::unique_ptrは、リソースの所有権を1つのunique_ptrだけが持つため、同じリソースを複数のポインタで共有することできません。

そのため、所有権がどこにあるかが明確になり、プログラムの動作が予測しやすくなります。

std::unique_ptrの特徴②:コピー不可

std::unique_ptrは、リソースの所有権を唯一のポインタが持つため、コピーはできません。

コピーはできませんが、std::moveを使用すると、所有権を別のunique_ptrに移動することができます。

std::unique_ptrの特徴③:自動解放

std::unique_ptrがスコープを抜けると、管理しているリソースは自動的に解放されます。

手動でdeleteを実施しなくてもリソースが解放されるので、メモリリークが防げます。

std::unique_ptrの使用方法

std::unique_ptrの使用方法です。

#include <iostream>
#include <memory>   // unique_ptrを使用するために必要

int main() {
  // unique_ptrを作成する
  std::unique_ptr<int> p(new int(100));

  std::cout << *p << std::endl;    // 出力値は100

  // deleteでのメモリ解放不要。スコープを抜けると自動的にメモリが解放される
}

その他の使用方法は、以下のページ参照

std::shared_ptrについて

std::shared_ptrは、複数のポインタでリソースを共有できるスマートポインタです。

std::shared_ptrの特徴①:共有所有権

std::shared_ptrは、複数のshared_ptrが同じリソースを共有して管理します。

リソースを使用する各ポインタが存在している間は、リソースは解放されません。

すべてのshared_ptrが破棄されると、リソースは自動的に解放されます。

std::shared_ptrの特徴②:参照カウント

std::shared_ptrは、内部的に参照カウントを保持しています。

shared_ptrがコピーされるたびに増加し、コピーが破棄されると減少します。

参照カウントが0になったとき、リソースが自動的に解放されます。

std::shared_ptrの特徴③:コピー可能

std::shared_ptrは、std::unique_ptrとは違いコピー可能です。

コピーが存在する間はリソースの解放は行われないため、同じリソースを複数の場所で使う場合に便利です。

std::shared_ptrの使用方法

std::shared_ptrの使用方法です。

#include <memory>
#include <iostream>

int main() {
  std::shared_ptr<int> p1(new int(10));    // 所有者は1人

  {
    // shared_ptrのコピー
    std::shared_ptr<int> p2 = p1;    // 所有者が2人

    std::cout << *p2 << std::endl;    // 出力値は、10
  }  // ここでp2がスコープを抜けて、所有者が1人になる(まだリソースは解放されない)

  std::cout << *p1 << std::endl;    // 出力値は、10
}  // ここでp1がスコープを抜けて、所有者が0人になる(リソースが解放される)

その他のstd::shared_ptrの使用方法は以下を参照

std::weak_ptrについて

std::weak_ptrは、リソースの所有権を持たないスマートポインタです。

std::weak_ptrの特徴①:所有権を持たない

std::weak_ptrは、参照するリソースの所有権を持たないため、参照カウントに影響を与えません。

shared_ptrで管理しているリソースをweak_ptrで参照しても参照カウントは増加しないため、リソースの自動解放を遅らせることなくリソースを参照することができます。

std::weak_ptrの特徴②:循環参照の防止

shared_ptr同士が互いに参照し合うと参照カウントがゼロにならず、リソースが解放されなくなる「循環参照」が発生します。

ここでweak_ptrを使用すると、所有権を持たない参照ができるため、循環参照を回避できます。

たとえば、親子関係のオブジェクトで、子オブジェクトが親オブジェクトをweak_ptrで参照すると、循環参照を防げます。

std::weak_ptrの特徴③:ロック機能

weak_ptrからリソースを利用するためには、lockメソッドを使用して一時的にshared_ptrに変換する必要があります。

lockメソッドを使用すると、リソースが有効な場合にはshared_ptrが返され、解放済みの場合はnullptrが返されます。

std::weak_ptrの使用方法

std::weak_ptrの使用方法です。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(20);
    std::weak_ptr<int> wp = sp;  // weak_ptr で shared_ptr を参照
    
    if (auto locked = wp.lock()) {  // weak_ptr を shared_ptr にロック
        std::cout << *locked << std::endl;  // 有効なら20を出力
    } else {
        std::cout << "リソースは解放されています" << std::endl;
    }
    
    sp.reset();  // shared_ptr を解放
    if (auto locked = wp.lock()) {
        std::cout << *locked << std::endl;
    } else {
        std::cout << "リソースは解放されています" << std::endl;
    }
}

C++ スマートポインタ まとめ

C++のスマートポインタは、メモリ管理を自動化し、手動でのメモリ解放によるメモリリークや二重解放を防ぐことができる便利な機能です。

スマートポインタには、以下の3つがあり、用途によって使い分けるかたちになります。

  • std::unique_ptr :所有権を1つのオブジェクトに限定するスマートポインタ
  • std::shared_ptr :複数の場所で所有権を共有できるスマートポインタ
  • std::weak_ptrstd::shred_ptrの循環参照を防ぐためのスマートポインタ

手動によるメモリ管理では、メモリリークや二重解放といった問題が発生しやすく、これらのメモリ関連の不具合は原因究明が難航することも多いため、スマートポインタを使用して自動化しましょう。

スポンサーリンク

  • この記事を書いた人

まさじぃ

ダメプログラマ歴17年です。 プログラミング関連の事や、 自分で使って良かったもの等の紹介をメインにやっています。

-プログラミング
-