<?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/es/tags/memory-management/</link><description>Recent content in Memory Management on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/memory-management/index.xml" rel="self" type="application/rss+xml"/><item><title>Guía práctica de punteros inteligentes (std::unique_ptr / shared_ptr) para prevenir fugas de memoria</title><link>http://kenji.blog/es/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/es/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 Guía práctica de punteros inteligentes (std::unique_ptr / shared_ptr) para prevenir fugas de memoria" />&lt;p>La gestión de memoria en C++ ha sido uno de los mayores desafíos para los desarrolladores durante muchos años. El estilo tradicional de gestión de memoria, que depende del uso manual de &lt;code>new&lt;/code> y &lt;code>delete&lt;/code>, era un caldo de cultivo para errores graves como fugas de memoria (memory leaks), punteros colgantes (dangling pointers) y dobles liberaciones. Sin embargo, con la llegada de Modern C++ (C++11 y posteriores), la situación ha cambiado drásticamente. El núcleo de este cambio son los &amp;ldquo;punteros inteligentes&amp;rdquo; (Smart Pointers).&lt;/p>
&lt;p>En este artículo, explicaremos con extremo detalle el funcionamiento y las técnicas avanzadas de uso de &lt;code>std::unique_ptr&lt;/code>, &lt;code>std::shared_ptr&lt;/code> y &lt;code>std::weak_ptr&lt;/code>, que son herramientas poderosas para erradicar las fugas de memoria y lograr una gestión de recursos segura y eficiente. Abordaremos su implementación interna (bloques de control y operaciones atómicas), su impacto en el rendimiento y la formulación matemática del conteo de referencias.&lt;/p>
&lt;h2 id="1-introducción-la-era-oscura-de-la-gestión-de-memoria-en-c-y-el-amanecer-de-modern-c">1. Introducción: La era oscura de la gestión de memoria en C++ y el amanecer de Modern C++
&lt;/h2>&lt;p>En el desarrollo tradicional de C++, la memoria asignada en el heap debía ser liberada bajo la propia responsabilidad del desarrollador.&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">// ... algún proceso ...
&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">// ¡Fuga de memoria! no se llama a 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>En el código anterior, si ocurre una excepción o se realiza un retorno anticipado (early return), se omite el &lt;code>delete&lt;/code>, provocando una fuga de memoria. El paradigma para evitar esto es &amp;ldquo;RAII (Resource Acquisition Is Initialization)&amp;rdquo;. RAII es una técnica que vincula la adquisición de recursos a la inicialización de un objeto (constructor) y la liberación de recursos a la destrucción del objeto (destructor). Los punteros inteligentes son un conjunto de clases de la biblioteca estándar que aplican este modismo RAII a la gestión de memoria.&lt;/p>
&lt;h2 id="2-stdunique_ptr-propiedad-exclusiva-sin-sobrecarga-zero-overhead">2. &lt;code>std::unique_ptr&lt;/code>: Propiedad exclusiva sin sobrecarga (zero-overhead)
&lt;/h2>&lt;p>&lt;code>std::unique_ptr&lt;/code> es un puntero inteligente que tiene &amp;ldquo;propiedad exclusiva&amp;rdquo; (Exclusive Ownership) sobre un objeto asignado dinámicamente. Solo puede haber un único &lt;code>unique_ptr&lt;/code> que posea un recurso determinado en un momento dado.&lt;/p>
&lt;h3 id="21-el-principio-de-cero-sobrecarga">2.1 El principio de cero sobrecarga
&lt;/h3>&lt;p>El mayor atractivo de &lt;code>std::unique_ptr&lt;/code> es su rendimiento. En su estado predeterminado, sin un eliminador (deleter) personalizado, el tamaño de &lt;code>std::unique_ptr&lt;/code> es exactamente el mismo que el de un puntero crudo (Raw Pointer). No tiene variables miembro innecesarias y no utiliza funciones virtuales. Gracias a la optimización del compilador, el acceso a través de un &lt;code>std::unique_ptr&lt;/code> se expande a un código ensamblador equivalente al de un puntero crudo.&lt;/p>
&lt;h3 id="22-transferencia-de-propiedad-y-stdmove">2.2 Transferencia de propiedad y &lt;code>std::move&lt;/code>
&lt;/h3>&lt;p>Dado que posee propiedad exclusiva, &lt;code>std::unique_ptr&lt;/code> no se puede copiar (el constructor de copia y el operador de asignación de copia están marcados como &lt;code>delete&lt;/code>). Para transferir la propiedad a otro &lt;code>unique_ptr&lt;/code>, se utiliza &lt;code>std::move&lt;/code> para aprovechar la semántica de movimiento (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">// Al salir del alcance, ptr se destruye y Resource también se libera
&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); // Error: no se puede copiar
&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">// Transferencia de propiedad
&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>El siguiente diagrama de Mermaid ilustra el concepto de transferencia de propiedad mediante &lt;code>std::move&lt;/code>.&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "Antes de std::move"
A["unique_ptr (ptr1)"] -->|"Posee"| B["Memoria Heap (Objeto)"]
end
subgraph "Después de std::move"
C["unique_ptr (ptr1)"] -.->|"Vacío (nullptr)"| D["nullptr"]
E["unique_ptr (ptr2)"] -->|"Posee"| F["Memoria Heap (Objeto)"]
end&lt;/div>
&lt;h3 id="23-implementación-de-un-eliminador-personalizado-custom-deleter">2.3 Implementación de un eliminador personalizado (custom deleter)
&lt;/h3>&lt;p>Al encapsular APIs heredadas de C (por ejemplo, &lt;code>FILE*&lt;/code> o sockets), es necesario llamar a funciones distintas de &lt;code>delete&lt;/code> (como &lt;code>fclose&lt;/code>) para liberar los recursos. &lt;code>std::unique_ptr&lt;/code> permite especificar un eliminador personalizado (custom deleter) en su segundo argumento de plantilla.&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">// Functor para el eliminador personalizado
&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">// Al finalizar el alcance, se llama a FileDeleter y se ejecuta 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>Si se utilizan punteros a funciones o expresiones lambda como eliminadores personalizados, el tamaño de &lt;code>unique_ptr&lt;/code> podría aumentar. Sin embargo, al usar un objeto de función (Functor) sin estado, como en el ejemplo anterior, el tamaño no aumenta respecto al de un puntero crudo gracias a la &lt;strong>EBCO (Empty Base Class Optimization)&lt;/strong> de C++ o a &lt;code>[[no_unique_address]]&lt;/code> de C++20 (se mantiene el cero sobrecarga).&lt;/p>
&lt;h2 id="3-stdshared_ptr-propiedad-compartida-y-bloques-de-control">3. &lt;code>std::shared_ptr&lt;/code>: Propiedad compartida y bloques de control
&lt;/h2>&lt;p>&lt;code>std::shared_ptr&lt;/code> es un puntero inteligente que permite que múltiples punteros compartan la propiedad de un mismo objeto. El objeto administrado se libera cuando se destruye el último &lt;code>shared_ptr&lt;/code>.&lt;/p>
&lt;h3 id="31-arquitectura-interna-el-bloque-de-control">3.1 Arquitectura interna: El bloque de control
&lt;/h3>&lt;p>&lt;code>std::shared_ptr&lt;/code> comparte metadatos asignados en el heap llamados &lt;strong>Bloque de control (Control Block)&lt;/strong>, separados del puntero al objeto administrado. El bloque de control contiene la siguiente información:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Strong Count (Conteo de referencias fuertes)&lt;/strong>: La cantidad de &lt;code>shared_ptr&lt;/code> que poseen el objeto. Cuando llega a 0, el objeto se destruye.&lt;/li>
&lt;li>&lt;strong>Weak Count (Conteo de referencias débiles)&lt;/strong>: La cantidad de &lt;code>weak_ptr&lt;/code> que observan el objeto. Cuando tanto el Strong Count como el Weak Count llegan a 0, se libera el propio bloque de control.&lt;/li>
&lt;li>&lt;strong>Eliminador personalizado y asignador&lt;/strong> (si se especifican).&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
A["std::shared_ptr&lt;T> (sp1)"] -->|"Puntero a T"| B["Objeto Administrado (T)"]
A -->|"Puntero al Bloque de Control"| C["Bloque de Control"]
D["std::shared_ptr&lt;T> (sp2)"] -->|"Puntero a T"| B
D -->|"Puntero al Bloque de Control"| C
C -->|"Elimina"| B
C -.->|"Strong Count: 2"| E["Strong Count"]
C -.->|"Weak Count: 0"| F["Weak Count"]
C -.->|"Eliminador Personalizado"| G["Eliminador (Deleter)"]&lt;/div>
&lt;p>Debido a esto, el tamaño del propio objeto &lt;code>std::shared_ptr&lt;/code> suele ser el doble del de un puntero crudo (un puntero al objeto y un puntero al bloque de control).&lt;/p>
&lt;h3 id="32-rendimiento-y-operaciones-atómicas">3.2 Rendimiento y operaciones atómicas
&lt;/h3>&lt;p>El conteo de referencias dentro del bloque de control está implementado con &lt;strong>operaciones atómicas (Atomic Operations)&lt;/strong> para que pueda incrementarse o decrementarse de manera segura incluso en entornos multihilo.&lt;/p>
&lt;p>En las arquitecturas x86/x64, se utilizan instrucciones atómicas como &lt;code>lock xadd&lt;/code> para incrementar y decrementar el conteo de referencias. Esto implica una sobrecarga de docenas de ciclos en comparación con una suma de enteros normal. Por lo tanto, si se pasa un &lt;code>shared_ptr&lt;/code> a una función por valor, se producirán incrementos y decrementos atómicos en cada copia, lo que degradará el rendimiento.&lt;/p>
&lt;p>&lt;strong>Mejor práctica&lt;/strong>: Al pasar un &lt;code>shared_ptr&lt;/code> a una función, a menos que sea necesario compartir la propiedad, se debe pasar como &lt;code>const std::shared_ptr&amp;lt;T&amp;gt;&amp;amp;&lt;/code> (referencia const) o bien pasar un puntero/referencia crudos.&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>Al crear un &lt;code>shared_ptr&lt;/code>, se debe utilizar &lt;code>std::make_shared&lt;/code> siempre que sea posible. Hay dos razones cruciales para ello.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Optimización de asignación de memoria&lt;/strong>:
Si se usa &lt;code>new&lt;/code>, ocurren dos asignaciones en el heap: una para el objeto en sí y otra para el bloque de control. Con &lt;code>std::make_shared&lt;/code>, se puede asignar un único bloque de memoria grande que incluye ambos en una sola asignación en el heap, lo que también mejora la eficiencia de la caché.&lt;/li>
&lt;li>&lt;strong>Seguridad contra excepciones&lt;/strong>:
En los estándares anteriores a C++17, el orden de evaluación de los argumentos de una función no estaba especificado. Por lo tanto, si ocurría una excepción al evaluar otros argumentos antes de pasar el puntero asignado con &lt;code>new&lt;/code> al constructor de &lt;code>shared_ptr&lt;/code>, existía el riesgo de una fuga de memoria. &lt;code>make_shared&lt;/code> evita completamente este problema.&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">// Práctica a evitar (2 asignaciones de memoria)
&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">// Práctica recomendada (1 asignación de memoria)
&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-resolución-de-referencias-circulares-y-observación">4. &lt;code>std::weak_ptr&lt;/code>: Resolución de referencias circulares y observación
&lt;/h2>&lt;p>La propiedad compartida tiene una debilidad fatal conocida como &amp;ldquo;referencias circulares&amp;rdquo; (Circular References). Si el Objeto A y el Objeto B se apuntan mutuamente con un &lt;code>shared_ptr&lt;/code>, el Strong Count de cada uno se mantendrá como mínimo en 1 y nunca llegará a 0 hasta que termine el programa, provocando una fuga de memoria.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Referencia Circular (Fuga de Memoria)"
A["Objeto A"] -->|"shared_ptr (Strong=1)"| B["Objeto B"]
B -->|"shared_ptr (Strong=1)"| A
end&lt;/div>
&lt;h3 id="41-rompiendo-el-ciclo-con-stdweak_ptr">4.1 Rompiendo el ciclo con &lt;code>std::weak_ptr&lt;/code>
&lt;/h3>&lt;p>&lt;code>std::weak_ptr&lt;/code> resuelve este problema. Se crea a partir de un &lt;code>shared_ptr&lt;/code> y hace referencia al objeto, pero &lt;strong>no incrementa el Strong Count&lt;/strong>. En su lugar, incrementa el Weak Count. Esto permite &amp;ldquo;observar&amp;rdquo; el objeto sin poseerlo.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Rompiendo la Referencia Circular"
C["Objeto A"] -->|"shared_ptr (Strong=1)"| D["Objeto B"]
D -.->|"weak_ptr (Weak=1)"| C
end&lt;/div>
&lt;h3 id="42-acceso-seguro-mediante-el-método-lock">4.2 Acceso seguro mediante el método &lt;code>lock()&lt;/code>
&lt;/h3>&lt;p>&lt;code>weak_ptr&lt;/code> no tiene operadores (&lt;code>-&amp;gt;&lt;/code> o &lt;code>*&lt;/code>) para acceder directamente al objeto, ya que el objeto destino podría haber sido destruido. Para acceder de forma segura, se debe llamar al método &lt;code>lock()&lt;/code>, el cual obtiene temporalmente un &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">// Se usa weak_ptr para evitar la referencia circular
&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">// Obtener un shared_ptr a partir de un weak_ptr para acceder
&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 y nodeB se destruyen adecuadamente
&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-restricciones-de-la-propiedad-compartida-en-entornos-multihilo">5. Restricciones de la propiedad compartida en entornos multihilo
&lt;/h2>&lt;p>La seguridad de hilos (thread-safety) de &lt;code>shared_ptr&lt;/code> suele malinterpretarse. Si bien &amp;ldquo;la actualización del conteo de referencias en el bloque de control es segura para hilos (thread-safe)&amp;rdquo;, &amp;ldquo;la lectura y escritura del propio objeto &lt;code>shared_ptr&lt;/code> no lo es&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Operación segura&lt;/strong>: Que múltiples hilos lean y escriban en &lt;em>sus propias&lt;/em> instancias de &lt;code>shared_ptr&lt;/code> (aunque compartan el mismo bloque de control).&lt;/li>
&lt;li>&lt;strong>Condición de carrera (Peligro)&lt;/strong>: Que múltiples hilos lean y escriban en la &lt;em>misma&lt;/em> instancia de &lt;code>shared_ptr&lt;/code> al mismo tiempo.&lt;/li>
&lt;/ul>
&lt;p>Si es necesario compartir la misma instancia entre múltiples hilos, se debe utilizar &lt;code>std::atomic&amp;lt;std::shared_ptr&amp;lt;T&amp;gt;&amp;gt;&lt;/code> (C++20) o protegerla con un mutex (&lt;code>std::mutex&lt;/code>).&lt;/p>
&lt;h2 id="6-formulación-matemática-del-conteo-de-referencias">6. Formulación matemática del conteo de referencias
&lt;/h2>&lt;p>La transición de estados del ciclo de vida en el bloque de control se puede expresar matemáticamente de la siguiente manera.
Sea $S(t)$ el Strong Count y $W(t)$ el Weak Count en el instante $t$.&lt;/p>
&lt;p>Estado inicial (inmediatamente después de &lt;code>make_shared&lt;/code>):
&lt;/p>
$$ S(0) = 1, \quad W(0) = 0 $$
&lt;p>Cuando ocurre una copia (duplicación del &lt;code>shared_ptr&lt;/code>):
&lt;/p>
$$ S(t_{next}) = S(t) + 1 $$
&lt;p>Condición para que el Objeto Administrado (Managed Object) sea destruido:
&lt;/p>
$$ \lim_{t \to t_d} S(t) = 0 $$
&lt;p>Condición para que el propio Bloque de Control (Control Block) sea liberado de la memoria:
&lt;/p>
$$ S(t) = 0 \quad \land \quad W(t) = 0 $$
&lt;p>
Es decir,
&lt;/p>
$$ S(t) + W(t) = 0 $$
&lt;p>Como muestra esta fórmula, mientras siga existiendo un &lt;code>weak_ptr&lt;/code> ($W(t) > 0$), el pequeño espacio de memoria para el bloque de control permanecerá asignado incluso si el objeto administrado ha sido destruido. Esta puede ser la única desventaja de &lt;code>make_shared&lt;/code> en algunos casos (dado que la memoria del objeto administrado y la del bloque de control están unificadas, si quedan referencias débiles, el espacio grande de memoria del objeto administrado tampoco se devuelve al sistema). Sin embargo, normalmente las ventajas de rendimiento de &lt;code>make_shared&lt;/code> superan esto de forma abrumadora.&lt;/p>
&lt;h2 id="7-conclusión">7. Conclusión
&lt;/h2>&lt;p>La gestión de memoria en Modern C++ ya no se encuentra en la era de administrar manualmente &lt;code>new&lt;/code>/&lt;code>delete&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>De forma predeterminada, utiliza siempre &lt;strong>&lt;code>std::unique_ptr&lt;/code>&lt;/strong> para incorporar una propiedad clara en el diseño mientras te beneficias del cero sobrecarga.&lt;/li>
&lt;li>Utiliza &lt;strong>&lt;code>std::shared_ptr&lt;/code>&lt;/strong> únicamente cuando realmente necesites compartir el ciclo de vida entre múltiples propietarios, y emplea &lt;code>std::make_shared&lt;/code> para su creación.&lt;/li>
&lt;li>Aprovecha &lt;strong>&lt;code>std::weak_ptr&lt;/code>&lt;/strong> para prevenir fugas de memoria en la implementación de patrones de observador o en estructuras de datos donde puedan ocurrir ciclos compartidos (referencias circulares).&lt;/li>
&lt;/ol>
&lt;p>Al comprender profundamente los punteros inteligentes y aplicarlos en el contexto adecuado, es posible construir una arquitectura de software segura y robusta sin sacrificar en absoluto el rendimiento de C++.&lt;/p></description></item></channel></rss>