スマートポインタ
[第]45[課では]moveセマンティクスについて学びました。
[現在]、[スマートポインタについて詳しく見ていきましょう――[現代の]C++における[メモリ管理の中核となるツール]です。
[スマートポインタを使えば]、[メモリリークを心配する必要は][もうありません]。
1. スマートポインタ[概要]
(1) 1.1 スマートポインタとは?
スマートポインタ[とは][動的メモリを]管理する[テンプレートクラス]であり、メモリを[自動的に解放]する(RAII)。
[3種類の]スマートポインタ:
| スマートポインタ | [機能] | [適用シーン] |
|---|---|---|
unique_ptr |
[独占所有権] | [唯一の所有]object |
shared_ptr |
[共有所有権] | [複数の場所から同一のオブジェクトにアクセスする必要がある] |
weak_ptr |
[弱]参照 | [解決]ループ参照 |
2. unique_ptr
基本的な使い方
unique_ptr [は][排他的所有権][を持つ]スマートポインタであり、[コピーはできず]、[移動のみ可能]です。
例:unique_ptr[基本用法] (難易度 ⭐)
▶ サンプル 2: codeexample (難易度 ⭐)
#include iostream
#include memory
int main() {
std::unique_ptrint p1(new int(42));
std::cout << *p1 << std::endl; // output:42
// std::unique_ptrint p2 = p1; // ❌!
std::unique_ptrint p2 = std::move(p1); // ✅
std::cout << *p2 << std::endl; // output:42
return 0;
} // p2
(2) 2.2 [カスタムデリーター]
例:[用]unique_ptr[管理]file (難易度 ⭐⭐)
#include iostream
#include memory
#include cstdio
//:
struct FileDeleter {
void operator()(FILE* fp) const {
if (fp) {
fclose(fp);
std::cout << "..." << std::endl;
}
}
};
int main() {
std::unique_ptr<FILE, FileDeleter> file(fopen("test.txt", "w"));
// fclose,unique_ptrFileDeleter
return 0;
}
3. shared_ptr
基本的な使い方
shared_ptr [は][所有権を共有する][スマートポインタ]であり、参照カウントを用いてメモリを[管理]します。
例:shared_ptr[基本用法] (難易度 ⭐)
#include iostream
#include memory
int main() {
std::shared_ptrint p1 = std::make_sharedint(42);
std::cout << ":" << p1.use_count() << std::endl; // 1
{
std::shared_ptrint p2 = p1; //,+1
std::cout << ":" << p1.use_count() << std::endl; // 2
} // p2,reference-1
std::cout << ":" << p1.use_count() << std::endl; // 1
return 0;
}
(2) 3.2 make_shared 対 new
[推奨用法]: std::make_shared [ではなく] new。
| [比較] | new |
make_shared |
|---|---|---|
| 例外[安全] | [漏洩の可能性あり] | [安全] |
| パフォーマンス | [二次分配] | [一次分配] |
| コード[簡潔] | [冗長] | [簡潔] |
// ...
auto p1 = std::make_sharedint(42);
// ...
std::shared_ptrint p2(new int(42));
4. weak_ptr
(1) 4.1 なぜ weak_ptr が必要なのか?
[問題]: shared_ptr [原因となり得る]ループ参照、メモリリーク。
例:loopreference(難易度 ⭐⭐⭐)
#include iostream
#include memory
struct Node {
std::shared_ptrNode next; //!
~Node() { std::cout << "Node destroyed" << std::endl; }
};
int main() {
auto n1 = std::make_sharedNode();
auto n2 = std::make_sharedNode();
n1->next = n2; // n1referencen2
n2->next = n1; // n2referencen1(reference)
return 0;
} // ❌ n1andn2(memory leaks)
(2) 4.2 [使用]weak_ptr[解消]ループ参照
[解決策]: [そのうちの1つ] shared_ptr [を] weak_ptr [に変更する]。
struct Node {
std::weak_ptrNode next; // weak_ptr,
~Node() { std::cout << "Node destroyed" << std::endl; }
};
int main() {
auto n1 = std::make_sharedNode();
auto n2 = std::make_sharedNode();
n1->next = n2; // ...
n2->next = n1;
return 0;
} // ✅
5. スマートポインタ[選択ガイド]
(1) 5.1 [選び方]?
| [シーン] | [おすすめ] |
|---|---|
| [唯一所有] | unique_ptr |
| [共有所有] | shared_ptr |
| [観察者] | weak_ptr [または元]ポインタ |
| 配列 | unique_ptr<T> |
(2) 5.2 [してはいけないこと]
| エラー | [説明] |
|---|---|
| [複数の原始]ポインタ[でshared_ptrを初期化してはいけません] | [重複した解放を引き起こすため] |
| [ポインタとスマートポインタを混在させてはならない] | [RAIIを破壊する] |
| [手動で呼び出さないでください]delete | [スマートポインタに]管理を任せる |
6. 演習:スマートポインタ[リソース管理]
▶ サンプル 1: [スマートポインタを使用して]データベース接続を管理する (難易度 ⭐⭐⭐)
#include iostream
#include memory
// ...
class DatabaseConnection {
public:
DatabaseConnection() {
std::cout << "..." << std::endl;
}
~DatabaseConnection() {
std::cout << "..." << std::endl;
}
void query(const std::string& sql) {
std::cout << "SQL:" << sql << std::endl;
}
};
int main() {
// unique_ptr
std::unique_ptrDatabaseConnection conn(new DatabaseConnection());
conn->query("SELECT * FROM users");
return 0;
} // ...
❓ よくある質問
unique_ptrの方が速い(参照カウントのオーバーヘッドがないため)、unique_ptrを優先して使用すること。Q:shared_ptrはスレッドセーフですか? A:- 参照カウントの変更はスレッドセーフです - ただし、参照先のオブジェクト自体はスレッドセーフではありません(ミューテックスによる保護が必要です)
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| unique_ptr | [排他所有権]、[コピー不可] |
| shared_ptr | [共有所有権]、参照[カウント] |
| weak_ptr | [弱]参照,[ループ参照の]解除 |
| make_shared | [推奨用法]、例外[安全] |
| [選択の原則] | [優先]unique_ptr、[必要な場合にのみ]shared_ptr |
📝 練習問題
-
**初心者向け(難易度 ⭐):[use] unique_ptr [動的に割り当てられた] int を [管理] し、[use] make_unique [で作成] します。[try] unique_ptr を [コピー] すると([should] コンパイル [fail] するはず)、[use] move に変更してください。
-
**中級(難易度 ⭐⭐):[用] shared_ptr [複数の] object [が同じリソースを共有する] ようにする。[2つの] shared_ptr [を同じ] object [に] 作成し、use_count() を出力して [参照] カウントの変化を[観察]する。
-
**上級(難易度 ⭐⭐⭐):[使用] weak_ptr [解決] shared_ptr のループ参照[問題]。[作成] クラス A [と] クラス B [が互いに] shared_ptr を保持し、[確認] メモリリークが発生する。[代わりに] weak_ptr を使用し、[正しく解放されることを確認]する。
- unique_ptr [排他的所有権]、[複製不可・移動可能]
- shared_ptr [共有所有権]、reference [参照カウント管理]
- weak_ptr [組み合わせ] shared_ptr [回避] ループ参照
- make_unique/make_shared の例外[安全な作成方法]
- [カスタムデリーターによる特殊リソースの解放処理]
[次のレッスン]:テンプレートメタプログラミング[入門](#47)



