<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Memory Management on kenji.blog</title><link>http://kenji.blog/zh-cn/tags/memory-management/</link><description>Recent content in Memory Management on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-cn/tags/memory-management/index.xml" rel="self" type="application/rss+xml"/><item><title>防止内存泄漏的智能指针（std::unique_ptr / shared_ptr）运用指南</title><link>http://kenji.blog/zh-cn/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/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> 的传统内存管理风格，成为了引发内存泄漏、悬垂指针以及重复释放等严重Bug的温床。然而，随着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> 拥有它。&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>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 "std::move 之前"
A["unique_ptr (ptr1)"] -->|"拥有"| B["堆内存（对象）"]
end
subgraph "std::move 之后"
C["unique_ptr (ptr1)"] -.->|"空（nullptr）"| D["nullptr"]
E["unique_ptr (ptr2)"] -->|"拥有"| F["堆内存（对象）"]
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> 可以通过第二个模板参数指定自定义删除器。&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>如果使用函数指针或Lambda表达式作为自定义删除器，可能会增加 &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> 的数量。当强引用计数和弱引用计数都变为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)"] -->|"指向 T 的指针"| B["受管理的对象（T）"]
A -->|"指向控制块的指针"| C["控制块"]
D["std::shared_ptr&lt;T> (sp2)"] -->|"指向 T 的指针"| B
D -->|"指向控制块的指针"| C
C -->|"删除"| B
C -.->|"强引用计数: 2"| E["强引用计数"]
C -.->|"弱引用计数: 0"| F["弱引用计数"]
C -.->|"自定义删除器"| G["删除器"]&lt;/div>
&lt;p>因此，&lt;code>std::shared_ptr&lt;/code> 对象本身的大小通常是裸指针的两倍（一个指向对象的指针，一个指向控制块的指针）。&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>。这有两个重要原因：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>内存分配优化&lt;/strong>：
使用 &lt;code>new&lt;/code> 会发生两次堆内存分配：一次是对象本身的分配，另一次是控制块的分配。而使用 &lt;code>std::make_shared&lt;/code>，可以通过一次堆内存分配同时分配包含两者的一大块内存，这也能提升缓存效率。&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">// 应该避免的写法（两次内存分配）
&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">// 推荐的写法（一次内存分配）
&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> 互相指向对方，它们的强引用计数将至少保持为1，直到程序结束也不会变为0，从而导致内存泄漏。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "循环引用（内存泄漏）"
A["对象 A"] -->|"shared_ptr (强引用=1)"| B["对象 B"]
B -->|"shared_ptr (强引用=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 "打破循环引用"
C["对象 A"] -->|"shared_ptr (强引用=1)"| D["对象 B"]
D -.->|"weak_ptr (弱引用=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>