C++ のスマートポインタ
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 15/104。
スマートポインタは、ヒープメモリを自動的に管理するラッパークラスです。ポインタがスコープ外になったときにメモリをクリーンアップすることで、手動での delete 呼び出しの必要性をなくします。
スマートポインタを使用するには、<memory>ヘッダーをインクルードしてください。
unique_ptr - 独占的な所有権。一度に一つのポインタのみがリソースを所有できます。
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // 通常のポインタのように使用します
// コピーはできませんが、所有権を移譲することはできます
std::unique_ptr<Player> player2 = std::move(player);
// player は nullptr になりますshared_ptr - 共有所有権。複数のポインタが同じリソースを所有できます。最後の所有者が破棄されたときにメモリが解放されます。
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // 両方がリソースを所有しています
std::cout << p1.use_count(); // 2を出力しますweak_ptr - shared_ptr の所有権を持たないオブザーバーです。循環参照を解決するのに役立ちます。
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // shared_ptrに変換
locked->attack();
}単一の所有権には、デフォルトで unique_ptr を使用します。複数のオブジェクトがリソースを共有する必要がある場合は、shared_ptr を使用します。生存期間を延長せずに監視するには、weak_ptr を使用します。
生の new よりも make_unique と make_shared を優先してください。
チャレンジ
簡単スマートポインタのさまざまな所有権モデルを実証するドキュメント共有システムを構築しましょう。unique_ptr がどのように排他的所有権を強制するか、shared_ptr がどのように複数の所有者を許可するか、そして weak_ptr が寿命に影響を与えずにどのように監視できるかを確認します。
コードを整理するために、2つのファイルを作成します。
Document.h: 共有可能なドキュメントを表すDocumentクラスを定義します。以下の内容を含める必要があります。- プライベートな
title属性 (string) - タイトルを受け取り、
"Document created: <title>"と出力するコンストラクタ "Document deleted: <title>"と出力するデストラクタ- タイトルを返す
getTitle()メソッド
- プライベートな
main.cpp: 入力からドキュメントのタイトルを読み取り、3つのスマートポインタタイプすべてを実証します。- 入力されたタイトルを持つ Document への
unique_ptrを作成します "Unique owner: <title>"と出力しますstd::moveを使用して、所有権を別のunique_ptrに移譲します"Ownership transferred"と出力します- タイトルが
"SharedDoc"である新しい Document へのshared_ptrを作成します - 同じドキュメントを指す2つ目の
shared_ptrを作成します use_count()を使用して"Shared owners: <count>"と出力します- 共有ポインタの1つから
weak_ptrを作成します lock()を使用して弱参照ポインタ経由でドキュメントにアクセスし、"Weak access: <title>"と出力します
- 入力されたタイトルを持つ Document への
スマートポインタを使用するために <memory> をインクルードすることを忘れないでください。スマートポインタの作成には std::make_unique と std::make_shared を使用してください。デストラクタのメッセージは、プログラムの終了時に各ドキュメントがいつ自動的にクリーンアップされるかを確認するのに役立ちます。
main.cpp で #include "Document.h" を使用してヘッダーファイルをインクルードしてください。
チートシート
スマートポインタは、ヒープメモリを自動的に管理するラッパークラスです。これらを使用するには、<memory> ヘッダーをインクルードしてください。
unique_ptr
独占的な所有権 - 一度に一つのポインタのみがリソースを所有できます:
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // 通常のポインタのように使用します
// std::move で所有権を移譲
std::unique_ptr<Player> player2 = std::move(player);
// player は nullptr になりますshared_ptr
共有された所有権 - 複数のポインタが同じリソースを所有できます。最後の所有者が破棄されたときにメモリが解放されます:
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // 両方がリソースを所有
std::cout << p1.use_count(); // 2 を出力weak_ptr
shared_ptr の所有権を持たないオブザーバーです。循環参照を解決するのに役立ちます:
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // shared_ptr に変換
locked->attack();
}ベストプラクティス: 単一の所有権にはデフォルトで unique_ptr を使用します。複数のオブジェクトがリソースを共有する必要がある場合は shared_ptr を使用します。生存期間を延長せずに監視するには weak_ptr を使用します。生の new よりも make_unique や make_shared を優先してください。
自分で試してみよう
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: inputTitle を使用して Document への unique_ptr を作成する
// std::make_unique を使用する
// TODO: "Unique owner: <title>" を出力する
// TODO: std::move を使用して、別の unique_ptr に所有権を移譲する
// TODO: "Ownership transferred" を出力する
// TODO: タイトルが "SharedDoc" の新しい Document への shared_ptr を作成する
// std::make_shared を使用する
// TODO: 同じドキュメントを指す 2 つ目の shared_ptr を作成する
// TODO: use_count() を使用して "Shared owners: <count>" を出力する
// TODO: いずれかの shared_ptr から weak_ptr を作成する
// TODO: lock() を使用して weak_ptr 経由でドキュメントにアクセスする
// そして "Weak access: <title>" を出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス