<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Smart Pointers on kenji.blog</title><link>http://kenji.blog/tags/smart-pointers/</link><description>Recent content in Smart Pointers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ja</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/tags/smart-pointers/index.xml" rel="self" type="application/rss+xml"/><item><title>メモリリークを防ぐスマートポインタ（std::unique_ptr / shared_ptr）の活用術</title><link>http://kenji.blog/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/p/cpp-smart-pointers-guide-unique-shared-ptr/</guid><description>&lt;img src="http://kenji.blog/p/cpp-smart-pointers-guide-unique-shared-ptr/img/eyecatch.jpg" alt="Featured image of post メモリリークを防ぐスマートポインタ（std::unique_ptr / shared_ptr）の活用術" />&lt;p>C++におけるメモリ管理は、長年にわたり開発者にとって最大の課題の一つでした。手動での &lt;code>new&lt;/code> と &lt;code>delete&lt;/code> に依存する従来のメモリ管理スタイルは、メモリリークやダングリングポインタ、二重解放といった深刻なバグを引き起こす温床となっていました。しかし、Modern C++（C++11以降）の登場により、状況は劇的に変化しました。その中核をなすのが「スマートポインタ（Smart Pointers）」です。&lt;/p>
&lt;p>本記事では、メモリリークを根絶し、安全かつ効率的なリソース管理を実現するための強力なツールである &lt;code>std::unique_ptr&lt;/code>、&lt;code>std::shared_ptr&lt;/code>、そして &lt;code>std::weak_ptr&lt;/code> の仕組みと高度な活用術について、内部実装（コントロールブロックやアトミック操作）、パフォーマンスへの影響、数学的モデルによる参照カウントの定式化を交えて極めて詳細に解説します。&lt;/p>
&lt;h2 id="1-導入cメモリ管理の暗黒時代とmodern-cの夜明け">1. 導入：C++メモリ管理の暗黒時代とModern C++の夜明け
&lt;/h2>&lt;p>かつてのC++開発では、ヒープ上に確保されたメモリは開発者自身が責任を持って解放する必要がありました。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">legacy_function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 何らかの処理 ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">some_condition&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// メモリリーク発生！ delete が呼ばれない
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上記のようなコードでは、例外が発生した場合や早期リターンが行われた場合に &lt;code>delete&lt;/code> がスキップされ、メモリリークが発生します。これを防ぐためのパラダイムが「RAII（Resource Acquisition Is Initialization）」です。RAIIは、リソースの確保をオブジェクトの初期化（コンストラクタ）に、リソースの解放をオブジェクトの破棄（デストラクタ）に結びつける手法です。スマートポインタは、このRAIIイディオムをメモリ管理に応用した標準ライブラリのクラススタックです。&lt;/p>
&lt;h2 id="2-stdunique_ptrゼロオーバーヘッドの排他的所有権">2. &lt;code>std::unique_ptr&lt;/code>：ゼロオーバーヘッドの排他的所有権
&lt;/h2>&lt;p>&lt;code>std::unique_ptr&lt;/code> は、動的に割り当てられたオブジェクトに対して「排他的な所有権（Exclusive Ownership）」を持つスマートポインタです。あるリソースを所有できる &lt;code>unique_ptr&lt;/code> は常に1つだけです。&lt;/p>
&lt;h3 id="21-ゼロオーバーヘッドの原則">2.1 ゼロオーバーヘッドの原則
&lt;/h3>&lt;p>&lt;code>std::unique_ptr&lt;/code> の最大の魅力は、そのパフォーマンスです。カスタムデリータを持たないデフォルトの状態では、&lt;code>std::unique_ptr&lt;/code> のサイズは生のポインタ（Raw Pointer）と完全に同一です。不要なメンバ変数は一切持たず、仮想関数も使用されていません。コンパイラの最適化により、&lt;code>std::unique_ptr&lt;/code> を介したアクセスは生のポインタと同等のアセンブリコードに展開されます。&lt;/p>
&lt;h3 id="22-所有権の移動と-stdmove">2.2 所有権の移動と &lt;code>std::move&lt;/code>
&lt;/h3>&lt;p>排他的所有権を持つため、&lt;code>std::unique_ptr&lt;/code> はコピーすることができません（コピーコンストラクタとコピー代入演算子が &lt;code>delete&lt;/code> されています）。所有権を別の &lt;code>unique_ptr&lt;/code> に移すには、&lt;code>std::move&lt;/code> を使用してムーブセマンティクス（Move Semantics）を利用します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Resource&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Resource&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Resource acquired&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Resource destroyed&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">do_something&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Doing something&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">process_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ptr&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">do_something&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// スコープを抜けると ptr が破棄され、Resource も解放される
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">my_ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// process_resource(my_ptr); // エラー：コピー不可
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">process_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// 所有権の移動
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;my_ptr is now empty.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のMermaid図は、&lt;code>std::move&lt;/code> による所有権の移動の概念を示しています。&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "Before std::move"
A["unique_ptr (ptr1)"] -->|"Owns"| B["Heap Memory (Object)"]
end
subgraph "After std::move"
C["unique_ptr (ptr1)"] -.->|"Empty (nullptr)"| D["nullptr"]
E["unique_ptr (ptr2)"] -->|"Owns"| F["Heap Memory (Object)"]
end&lt;/div>
&lt;h3 id="23-カスタムデリータの実装">2.3 カスタムデリータの実装
&lt;/h3>&lt;p>C言語のレガシーAPI（例えば &lt;code>FILE*&lt;/code> やソケットなど）をラップする際、メモリの解放に &lt;code>delete&lt;/code> 以外の関数（&lt;code>fclose&lt;/code> など）を呼ぶ必要があります。&lt;code>std::unique_ptr&lt;/code> は第2テンプレート引数にカスタムデリータを指定できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdio&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// カスタムデリータ用のファンクタ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">FileDeleter&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">operator&lt;/span>&lt;span class="p">()(&lt;/span>&lt;span class="n">FILE&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">fp&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fp&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Closing file.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fp&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="n">UniqueFile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">FILE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">FileDeleter&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">UniqueFile&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fputs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello, Smart Pointers!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// スコープ終了時に FileDeleter が呼ばれ fclose される
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>カスタムデリータとして関数ポインタやラムダ式を使用すると &lt;code>unique_ptr&lt;/code> のサイズが増加する可能性がありますが、上記のようにステートレスな関数オブジェクト（Functor）を使用すると、C++の**EBCO（Empty Base Class Optimization）**またはC++20の &lt;code>[[no_unique_address]]&lt;/code> によりサイズは生のポインタから増加しません（ゼロオーバーヘッドが維持されます）。&lt;/p>
&lt;h2 id="3-stdshared_ptr共有所有権とコントロールブロック">3. &lt;code>std::shared_ptr&lt;/code>：共有所有権とコントロールブロック
&lt;/h2>&lt;p>&lt;code>std::shared_ptr&lt;/code> は、複数のポインタが同一のオブジェクトを共有して所有するためのスマートポインタです。最後の &lt;code>shared_ptr&lt;/code> が破棄されたときに、管理しているオブジェクトが解放されます。&lt;/p>
&lt;h3 id="31-内部アーキテクチャコントロールブロック">3.1 内部アーキテクチャ：コントロールブロック
&lt;/h3>&lt;p>&lt;code>std::shared_ptr&lt;/code> は、管理対象のオブジェクトへのポインタとは別に、**コントロールブロック（Control Block）**と呼ばれるメタデータをヒープ上に割り当てて共有します。コントロールブロックには以下の情報が含まれます：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Strong Count (強参照カウント)&lt;/strong>：オブジェクトを所有している &lt;code>shared_ptr&lt;/code> の数。これが0になるとオブジェクトが破棄されます。&lt;/li>
&lt;li>&lt;strong>Weak Count (弱参照カウント)&lt;/strong>：オブジェクトを監視している &lt;code>weak_ptr&lt;/code> の数。Strong CountとWeak Countの両方が0になると、コントロールブロック自体が解放されます。&lt;/li>
&lt;li>&lt;strong>カスタムデリータとアロケータ&lt;/strong>（指定された場合）。&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
A["std::shared_ptr&lt;T> (sp1)"] -->|"Pointer to T"| B["Managed Object (T)"]
A -->|"Pointer to Control Block"| C["Control Block"]
D["std::shared_ptr&lt;T> (sp2)"] -->|"Pointer to T"| B
D -->|"Pointer to Control Block"| C
C -->|"Deletes"| B
C -.->|"Strong Count: 2"| E["Strong Count"]
C -.->|"Weak Count: 0"| F["Weak Count"]
C -.->|"Custom Deleter"| G["Deleter"]&lt;/div>
&lt;p>このため、&lt;code>std::shared_ptr&lt;/code> オブジェクト自体のサイズは通常、生のポインタの2倍（オブジェクトへのポインタと、コントロールブロックへのポインタ）になります。&lt;/p>
&lt;h3 id="32-パフォーマンスとアトミック操作">3.2 パフォーマンスとアトミック操作
&lt;/h3>&lt;p>コントロールブロック内の参照カウントは、マルチスレッド環境でも安全に増減できるよう、**アトミック操作（Atomic Operations）**として実装されています。&lt;/p>
&lt;p>x86/x64アーキテクチャでは、参照カウントの増減には &lt;code>lock xadd&lt;/code> のようなアトミック命令が使用されます。これは通常の整数加算に比べて数十サイクルのオーバーヘッドを伴います。したがって、値渡しで &lt;code>shared_ptr&lt;/code> を関数に渡すと、コピーのたびにアトミックなインクリメントとデクリメントが発生し、パフォーマンスが低下します。&lt;/p>
&lt;p>&lt;strong>ベストプラクティス&lt;/strong>：&lt;code>shared_ptr&lt;/code> を関数に渡す際は、所有権を共有する必要がない限り &lt;code>const std::shared_ptr&amp;lt;T&amp;gt;&amp;amp;&lt;/code>（const参照）として渡すか、生のポインタ/参照を渡すべきです。&lt;/p>
&lt;h3 id="33-stdmake_shared-vs-new">3.3 &lt;code>std::make_shared&lt;/code> vs &lt;code>new&lt;/code>
&lt;/h3>&lt;p>&lt;code>shared_ptr&lt;/code> を生成する際は、可能な限り &lt;code>std::make_shared&lt;/code> を使用すべきです。これには2つの重大な理由があります。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>メモリ割り当ての最適化&lt;/strong>：
&lt;code>new&lt;/code> を使用すると、オブジェクト本体の割り当てとコントロールブロックの割り当ての2回のヒープアロケーションが発生します。&lt;code>std::make_shared&lt;/code> を使用すると、両方を包含する1つの大きなメモリブロックを1回のヒープアロケーションで確保でき、キャッシュ効率も向上します。&lt;/li>
&lt;li>&lt;strong>例外安全性&lt;/strong>：
C++17より前の規格では、関数の引数評価順序が未規定であったため、&lt;code>new&lt;/code> で確保したポインタを &lt;code>shared_ptr&lt;/code> のコンストラクタに渡す前に他の引数の評価で例外が発生すると、メモリリークのリスクがありました。&lt;code>make_shared&lt;/code> はこの問題を完全に回避します。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 避けるべき書き方 (2回のメモリアロケーション)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 推奨される書き方 (1回のメモリアロケーション)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="4-stdweak_ptr循環参照の解決と監視">4. &lt;code>std::weak_ptr&lt;/code>：循環参照の解決と監視
&lt;/h2>&lt;p>共有所有権には「循環参照（Circular References）」という致命的な弱点があります。オブジェクトAとオブジェクトBが互いに &lt;code>shared_ptr&lt;/code> で指し合っている場合、それぞれのStrong Countは最低でも1に維持され、プログラムが終了するまで決して0にならず、メモリリークが発生します。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Circular Reference (Memory Leak)"
A["Object A"] -->|"shared_ptr (Strong=1)"| B["Object B"]
B -->|"shared_ptr (Strong=1)"| A
end&lt;/div>
&lt;h3 id="41-stdweak_ptr-による循環の打破">4.1 &lt;code>std::weak_ptr&lt;/code> による循環の打破
&lt;/h3>&lt;p>この問題を解決するのが &lt;code>std::weak_ptr&lt;/code> です。&lt;code>weak_ptr&lt;/code> は &lt;code>shared_ptr&lt;/code> から作成され、オブジェクトを参照しますが、&lt;strong>Strong Countを増加させません&lt;/strong>。代わりにWeak Countを増加させます。これにより、所有権を持たずにオブジェクトを「監視」することができます。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Breaking Circular Reference"
C["Object A"] -->|"shared_ptr (Strong=1)"| D["Object B"]
D -.->|"weak_ptr (Weak=1)"| C
end&lt;/div>
&lt;h3 id="42-lock-メソッドによる安全なアクセス">4.2 &lt;code>lock()&lt;/code> メソッドによる安全なアクセス
&lt;/h3>&lt;p>&lt;code>weak_ptr&lt;/code> は直接オブジェクトにアクセスする演算子（&lt;code>-&amp;gt;&lt;/code> や &lt;code>*&lt;/code>）を持っていません。対象のオブジェクトがすでに破棄されている可能性があるためです。安全にアクセスするには、&lt;code>lock()&lt;/code> メソッドを呼び出して一時的に &lt;code>shared_ptr&lt;/code> を取得します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Node&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">next&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">weak_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">prev&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 循環参照を防ぐためにweak_ptrを使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Created &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Destroyed &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">nodeA&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;A&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">nodeB&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nodeA&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">next&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeB&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nodeB&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">prev&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeA&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// weak_ptrからshared_ptrを取得してアクセス
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span> &lt;span class="n">locked_prev&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeB&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">prev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Node B&amp;#39;s prev is &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">locked_prev&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Node B&amp;#39;s prev is already destroyed.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// nodeAとnodeBは適切に破棄される
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-マルチスレッド環境における共有所有権の制約">5. マルチスレッド環境における共有所有権の制約
&lt;/h2>&lt;p>&lt;code>shared_ptr&lt;/code> のスレッドセーフティについては誤解されがちです。「コントロールブロック内の参照カウントの更新はスレッドセーフ」ですが、「&lt;code>shared_ptr&lt;/code> オブジェクト自体の読み書きはスレッドセーフではありません」。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>安全な操作&lt;/strong>：複数のスレッドが、&lt;em>それぞれ自身の&lt;/em> &lt;code>shared_ptr&lt;/code> インスタンス（ただし同じコントロールブロックを共有している）を読み書きすること。&lt;/li>
&lt;li>&lt;strong>データレース（危険）&lt;/strong>：複数のスレッドが、&lt;em>全く同じ&lt;/em> &lt;code>shared_ptr&lt;/code> インスタンスに対して同時に読み書きすること。&lt;/li>
&lt;/ul>
&lt;p>同じインスタンスを複数のスレッドで共有する必要がある場合は、&lt;code>std::atomic&amp;lt;std::shared_ptr&amp;lt;T&amp;gt;&amp;gt;&lt;/code>（C++20）を使用するか、ミューテックス（&lt;code>std::mutex&lt;/code>）で保護する必要があります。&lt;/p>
&lt;h2 id="6-参照カウントの数学的定式化">6. 参照カウントの数学的定式化
&lt;/h2>&lt;p>コントロールブロックにおけるライフサイクルの状態遷移を数学的に表現すると以下のようになります。
時刻 $t$ における Strong Count を $S(t)$、Weak Count を $W(t)$ とします。&lt;/p>
&lt;p>初期状態（&lt;code>make_shared&lt;/code> 直後）：
&lt;/p>
$$ S(0) = 1, \quad W(0) = 0 $$
&lt;p>コピー（&lt;code>shared_ptr&lt;/code> の複製）が行われると：
&lt;/p>
$$ S(t_{next}) = S(t) + 1 $$
&lt;p>管理オブジェクト（Managed Object）が破棄される条件：
&lt;/p>
$$ \lim_{t \to t_d} S(t) = 0 $$
&lt;p>コントロールブロック（Control Block）自身がメモリから解放される条件：
&lt;/p>
$$ S(t) = 0 \quad \land \quad W(t) = 0 $$
&lt;p>
つまり、
&lt;/p>
$$ S(t) + W(t) = 0 $$
&lt;p>この数式が示すように、&lt;code>weak_ptr&lt;/code> が存在し続ける限り（$W(t) > 0$）、管理オブジェクトが破棄されていてもコントロールブロック用の小さなメモリ空間は確保され続けます。これが &lt;code>make_shared&lt;/code> の唯一の欠点（管理オブジェクトのメモリとコントロールブロックが一体化しているため、弱い参照が残っていると管理オブジェクト用の巨大なメモリ空間もシステムに返還されない）となるケースがありますが、通常は &lt;code>make_shared&lt;/code> のパフォーマンス上の利点が圧倒的に上回ります。&lt;/p>
&lt;h2 id="7-結論">7. 結論
&lt;/h2>&lt;p>Modern C++におけるメモリ管理は、もはや手動で &lt;code>new&lt;/code>/&lt;code>delete&lt;/code> を管理する時代ではありません。&lt;/p>
&lt;ol>
&lt;li>デフォルトでは常に &lt;strong>&lt;code>std::unique_ptr&lt;/code>&lt;/strong> を使用し、ゼロオーバーヘッドの恩恵を受けつつ明確な所有権を設計に組み込みます。&lt;/li>
&lt;li>本当に複数の所有者間でライフサイクルを共有する必要がある場合にのみ &lt;strong>&lt;code>std::shared_ptr&lt;/code>&lt;/strong> を使用し、生成には &lt;code>std::make_shared&lt;/code> を用います。&lt;/li>
&lt;li>共有の環（循環参照）が発生しうるデータ構造やオブザーバーパターンの実装には、&lt;strong>&lt;code>std::weak_ptr&lt;/code>&lt;/strong> を活用してメモリリークを未然に防ぎます。&lt;/li>
&lt;/ol>
&lt;p>スマートポインタを深く理解し、適材適所で活用することで、C++のパフォーマンスを一切犠牲にすることなく、安全で堅牢なソフトウェアアーキテクチャを構築することが可能になります。&lt;/p></description></item></channel></rss>