<?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/ru/tags/memory-management/</link><description>Recent content in Memory Management on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/tags/memory-management/index.xml" rel="self" type="application/rss+xml"/><item><title>Использование умных указателей (std::unique_ptr / shared_ptr) для предотвращения утечек памяти</title><link>http://kenji.blog/ru/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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>, служил рассадником серьезных ошибок, таких как утечки памяти, висячие указатели (dangling pointers) и двойное освобождение (double free). Однако с появлением 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++ память, выделяемую в куче (heap), разработчик должен был освобождать самостоятельно.&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>В таком коде, если возникает исключение или происходит ранний возврат (early return), вызов &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> — его производительность. В состоянии по умолчанию, без кастомного удалителя (custom deleter), размер &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>, необходимо использовать семантику перемещения (Move Semantics) с помощью &lt;code>std::move&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;/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-реализация-пользовательского-удалителя-custom-deleter">2.3 Реализация пользовательского удалителя (Custom Deleter)
&lt;/h3>&lt;p>При оборачивании устаревших API на C (например, &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>Использование указателей на функции или лямбда-выражений в качестве пользовательского удалителя может увеличить размер &lt;code>unique_ptr&lt;/code>, однако при использовании функционального объекта без состояния (Functor), как показано выше, благодаря &lt;strong>EBCO (Empty Base Class Optimization)&lt;/strong> в C++ или &lt;code>[[no_unique_address]]&lt;/code> в C++20, размер не будет превышать размер сырого указателя (поддерживаются нулевые накладные расходы).&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> выделяет в куче и совместно использует метаданные, называемые &lt;strong>управляющим блоком (Control Block)&lt;/strong>. Управляющий блок содержит следующую информацию:&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)"] -->|"Указатель на T"| B["Управляемый объект (T)"]
A -->|"Указатель на управляющий блок"| C["Управляющий блок"]
D["std::shared_ptr&lt;T> (sp2)"] -->|"Указатель на T"| B
D -->|"Указатель на управляющий блок"| C
C -->|"Удаляет"| B
C -.->|"Strong Count: 2"| E["Strong Count"]
C -.->|"Weak Count: 0"| F["Weak Count"]
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>Счетчики ссылок в управляющем блоке реализованы с помощью &lt;strong>атомарных операций (Atomic Operations)&lt;/strong>, чтобы их можно было безопасно увеличивать и уменьшать в многопоточной среде.&lt;/p>
&lt;p>На архитектурах x86/x64 для инкремента и декремента счетчика ссылок используются атомарные инструкции, такие как &lt;code>lock xadd&lt;/code>. Это влечет за собой накладные расходы в десятки тактов по сравнению с обычным целочисленным сложением. Следовательно, передача &lt;code>shared_ptr&lt;/code> в функцию по значению вызывает атомарный инкремент и декремент при каждом копировании, что снижает производительность.&lt;/p>
&lt;p>&lt;strong>Лучшая практика (Best Practice)&lt;/strong>: При передаче &lt;code>shared_ptr&lt;/code> в функцию его следует передавать как константную ссылку (&lt;code>const std::shared_ptr&amp;lt;T&amp;gt;&amp;amp;&lt;/code>) или передавать сырой указатель/ссылку, если нет необходимости разделять владение.&lt;/p>
&lt;h3 id="33-stdmake_shared-против-new">3.3 &lt;code>std::make_shared&lt;/code> против &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>Безопасность исключений (Exception Safety)&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 "Циклическая ссылка (Утечка памяти)"
A["Объект A"] -->|"shared_ptr (Strong=1)"| B["Объект 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 "Разрыв циклической ссылки"
C["Объект A"] -->|"shared_ptr (Strong=1)"| D["Объект 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>shared_ptr&lt;/code>, вызвав метод &lt;code>lock()&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">// Получение shared_ptr из weak_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>Жизненный цикл в управляющем блоке можно математически выразить следующим образом.
Пусть $S(t)$ — Strong Count в момент времени $t$, а $W(t)$ — Weak Count.&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>Для реализации структур данных или паттерна &amp;ldquo;Наблюдатель&amp;rdquo; (Observer), где могут возникать циклы совместного владения (циклические ссылки), применяйте &lt;strong>&lt;code>std::weak_ptr&lt;/code>&lt;/strong>, чтобы предотвратить утечки памяти.&lt;/li>
&lt;/ol>
&lt;p>Глубокое понимание умных указателей и их применение в подходящих ситуациях позволяет создавать безопасную и надежную архитектуру программного обеспечения без каких-либо потерь в производительности C++.&lt;/p></description></item></channel></rss>