<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Smart Pointers on kenji.blog</title><link>http://kenji.blog/de/tags/smart-pointers/</link><description>Recent content in Smart Pointers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/tags/smart-pointers/index.xml" rel="self" type="application/rss+xml"/><item><title>Nutzung von Smart Pointern (std::unique_ptr / shared_ptr) zur Vermeidung von Speicherlecks</title><link>http://kenji.blog/de/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/de/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 Nutzung von Smart Pointern (std::unique_ptr / shared_ptr) zur Vermeidung von Speicherlecks" />&lt;p>Die Speicherverwaltung in C++ war lange Zeit eine der größten Herausforderungen für Entwickler. Der traditionelle Speicherverwaltungsstil, der auf manuellem &lt;code>new&lt;/code> und &lt;code>delete&lt;/code> beruhte, war eine Brutstätte für schwerwiegende Fehler wie Speicherlecks (Memory Leaks), baumelnde Zeiger (Dangling Pointers) und doppelte Freigaben (Double Frees). Mit dem Aufkommen von Modern C++ (seit C++11) hat sich die Situation jedoch dramatisch verändert. Den Kern dieser Veränderung bilden die &amp;ldquo;Smart Pointer&amp;rdquo; (Intelligente Zeiger).&lt;/p>
&lt;p>In diesem Artikel werden die Funktionsweise und fortgeschrittene Nutzungstechniken von &lt;code>std::unique_ptr&lt;/code>, &lt;code>std::shared_ptr&lt;/code> und &lt;code>std::weak_ptr&lt;/code> – leistungsstarken Werkzeugen zur Beseitigung von Speicherlecks und zur Realisierung einer sicheren und effizienten Ressourcenverwaltung – äußerst detailliert erläutert. Dabei gehen wir auch auf die interne Implementierung (Kontrollblöcke und atomare Operationen), die Auswirkungen auf die Leistung sowie die Formulierung der Referenzzählung durch mathematische Modelle ein.&lt;/p>
&lt;h2 id="1-einführung-das-dunkle-zeitalter-der-c-speicherverwaltung-und-die-morgendämmerung-von-modern-c">1. Einführung: Das dunkle Zeitalter der C++-Speicherverwaltung und die Morgendämmerung von Modern C++
&lt;/h2>&lt;p>In der früheren C++-Entwicklung mussten Entwickler selbst dafür sorgen, dass auf dem Heap reservierter Speicher wieder freigegeben wurde.&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">// ... Irgendeine Verarbeitung ...
&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">// Speicherleck tritt auf! delete wird nicht aufgerufen
&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>In Code wie dem obigen wird &lt;code>delete&lt;/code> übersprungen, wenn eine Ausnahme auftritt oder eine frühzeitige Rückkehr (Early Return) stattfindet, was zu einem Speicherleck führt. Das Paradigma, um dies zu verhindern, ist &amp;ldquo;RAII (Resource Acquisition Is Initialization)&amp;rdquo;. RAII ist eine Technik, die die Zuweisung von Ressourcen an die Initialisierung eines Objekts (Konstruktor) und die Freigabe von Ressourcen an die Zerstörung des Objekts (Destruktor) bindet. Smart Pointer sind ein Klassen-Stack der Standardbibliothek, der dieses RAII-Idiom auf die Speicherverwaltung anwendet.&lt;/p>
&lt;h2 id="2-stdunique_ptr-exklusives-eigentum-ohne-overhead-zero-overhead">2. &lt;code>std::unique_ptr&lt;/code>: Exklusives Eigentum ohne Overhead (Zero-Overhead)
&lt;/h2>&lt;p>&lt;code>std::unique_ptr&lt;/code> ist ein Smart Pointer, der &amp;ldquo;exklusives Eigentum&amp;rdquo; (Exclusive Ownership) an einem dynamisch zugewiesenen Objekt besitzt. Es kann immer nur ein einziger &lt;code>unique_ptr&lt;/code> eine bestimmte Ressource besitzen.&lt;/p>
&lt;h3 id="21-das-prinzip-des-zero-overhead">2.1 Das Prinzip des Zero-Overhead
&lt;/h3>&lt;p>Der größte Reiz von &lt;code>std::unique_ptr&lt;/code> ist seine Leistung. Im Standardzustand, ohne benutzerdefinierten Deleter (Custom Deleter), ist die Größe von &lt;code>std::unique_ptr&lt;/code> exakt identisch mit der eines rohen Zeigers (Raw Pointer). Er besitzt keine unnötigen Elementvariablen und es werden keine virtuellen Funktionen verwendet. Durch Compiler-Optimierungen wird der Zugriff über &lt;code>std::unique_ptr&lt;/code> in denselben Assembler-Code übersetzt wie der eines rohen Zeigers.&lt;/p>
&lt;h3 id="22-eigentumsübertragung-und-stdmove">2.2 Eigentumsübertragung und &lt;code>std::move&lt;/code>
&lt;/h3>&lt;p>Da er exklusives Eigentum besitzt, kann &lt;code>std::unique_ptr&lt;/code> nicht kopiert werden (Kopierkonstruktor und Kopierzuweisungsoperator sind als &lt;code>delete&lt;/code> markiert). Um das Eigentum auf einen anderen &lt;code>unique_ptr&lt;/code> zu übertragen, wird &lt;code>std::move&lt;/code> verwendet, um Move-Semantik (Move Semantics) zu nutzen.&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">// Wenn der Gültigkeitsbereich (Scope) verlassen wird, wird ptr zerstört und auch Resource freigegeben
&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); // Fehler: Kopieren nicht möglich
&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">// Eigentumsübertragung
&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>Das folgende Mermaid-Diagramm veranschaulicht das Konzept der Eigentumsübertragung durch &lt;code>std::move&lt;/code>.&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "Vor std::move"
A["unique_ptr (ptr1)"] -->|"Besitzt"| B["Heap-Speicher (Objekt)"]
end
subgraph "Nach std::move"
C["unique_ptr (ptr1)"] -.->|"Leer (nullptr)"| D["nullptr"]
E["unique_ptr (ptr2)"] -->|"Besitzt"| F["Heap-Speicher (Objekt)"]
end&lt;/div>
&lt;h3 id="23-implementierung-eines-benutzerdefinierten-deleters">2.3 Implementierung eines benutzerdefinierten Deleters
&lt;/h3>&lt;p>Wenn man Legacy-APIs aus C (wie z. B. &lt;code>FILE*&lt;/code> oder Sockets) verpackt, muss man für die Speicherfreigabe andere Funktionen als &lt;code>delete&lt;/code> aufrufen (z. B. &lt;code>fclose&lt;/code>). &lt;code>std::unique_ptr&lt;/code> ermöglicht es, im zweiten Template-Argument einen benutzerdefinierten Deleter anzugeben.&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">// Funktor für benutzerdefinierten Deleter
&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">// Am Ende des Gültigkeitsbereichs wird FileDeleter aufgerufen und fclose ausgeführt
&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>Die Verwendung von Funktionszeigern oder Lambda-Ausdrücken als benutzerdefinierte Deleter kann die Größe von &lt;code>unique_ptr&lt;/code> erhöhen. Wenn man jedoch, wie oben gezeigt, zustandslose Funktionsobjekte (Funktoren) verwendet, erhöht sich die Größe durch &lt;strong>EBCO (Empty Base Class Optimization)&lt;/strong> in C++ oder &lt;code>[[no_unique_address]]&lt;/code> in C++20 nicht gegenüber einem rohen Zeiger (der Zero-Overhead bleibt erhalten).&lt;/p>
&lt;h2 id="3-stdshared_ptr-gemeinsames-eigentum-und-kontrollblock">3. &lt;code>std::shared_ptr&lt;/code>: Gemeinsames Eigentum und Kontrollblock
&lt;/h2>&lt;p>&lt;code>std::shared_ptr&lt;/code> ist ein Smart Pointer, der es mehreren Zeigern ermöglicht, dasselbe Objekt gemeinsam zu besitzen. Wenn der letzte &lt;code>shared_ptr&lt;/code> zerstört wird, wird das verwaltete Objekt freigegeben.&lt;/p>
&lt;h3 id="31-interne-architektur-der-kontrollblock">3.1 Interne Architektur: Der Kontrollblock
&lt;/h3>&lt;p>&lt;code>std::shared_ptr&lt;/code> weist neben dem Zeiger auf das verwaltete Objekt Metadaten, den sogenannten &lt;strong>Kontrollblock (Control Block)&lt;/strong>, auf dem Heap zu und teilt diese. Der Kontrollblock enthält die folgenden Informationen:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Strong Count (Starke Referenzzählung)&lt;/strong>: Die Anzahl der &lt;code>shared_ptr&lt;/code>, die das Objekt besitzen. Wenn diese 0 erreicht, wird das Objekt zerstört.&lt;/li>
&lt;li>&lt;strong>Weak Count (Schwache Referenzzählung)&lt;/strong>: Die Anzahl der &lt;code>weak_ptr&lt;/code>, die das Objekt überwachen. Wenn sowohl der Strong Count als auch der Weak Count 0 erreichen, wird der Kontrollblock selbst freigegeben.&lt;/li>
&lt;li>&lt;strong>Benutzerdefinierter Deleter und Allokator&lt;/strong> (falls angegeben).&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
A["std::shared_ptr&lt;T> (sp1)"] -->|"Zeiger auf T"| B["Verwaltetes Objekt (T)"]
A -->|"Zeiger auf Kontrollblock"| C["Kontrollblock"]
D["std::shared_ptr&lt;T> (sp2)"] -->|"Zeiger auf T"| B
D -->|"Zeiger auf Kontrollblock"| C
C -->|"Löscht"| B
C -.->|"Strong Count: 2"| E["Strong Count"]
C -.->|"Weak Count: 0"| F["Weak Count"]
C -.->|"Benutzerdefinierter Deleter"| G["Deleter"]&lt;/div>
&lt;p>Aus diesem Grund ist die Größe des &lt;code>std::shared_ptr&lt;/code>-Objekts selbst normalerweise doppelt so groß wie ein roher Zeiger (ein Zeiger auf das Objekt und ein Zeiger auf den Kontrollblock).&lt;/p>
&lt;h3 id="32-leistung-und-atomare-operationen">3.2 Leistung und atomare Operationen
&lt;/h3>&lt;p>Die Referenzzählungen im Kontrollblock sind als &lt;strong>atomare Operationen (Atomic Operations)&lt;/strong> implementiert, sodass sie auch in Multithreading-Umgebungen sicher inkrementiert und dekrementiert werden können.&lt;/p>
&lt;p>Auf x86/x64-Architekturen werden für das Inkrementieren und Dekrementieren der Referenzzählung atomare Befehle wie &lt;code>lock xadd&lt;/code> verwendet. Dies geht im Vergleich zur normalen Ganzzahl-Addition mit einem Overhead von mehreren Dutzend Zyklen einher. Wenn man also &lt;code>shared_ptr&lt;/code> per Wertübergabe (Pass-by-Value) an eine Funktion übergibt, erfolgen bei jedem Kopiervorgang ein atomares Inkrement und Dekrement, was die Leistung beeinträchtigt.&lt;/p>
&lt;p>&lt;strong>Best Practice&lt;/strong>: Wenn man einen &lt;code>shared_ptr&lt;/code> an eine Funktion übergibt, sollte man ihn entweder als &lt;code>const std::shared_ptr&amp;lt;T&amp;gt;&amp;amp;&lt;/code> (const-Referenz) oder als rohen Zeiger/Referenz übergeben, es sei denn, man muss das Eigentum teilen.&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>Bei der Erstellung von &lt;code>shared_ptr&lt;/code> sollte man nach Möglichkeit &lt;code>std::make_shared&lt;/code> verwenden. Dafür gibt es zwei wesentliche Gründe:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Optimierung der Speicherzuweisung&lt;/strong>:
Wenn man &lt;code>new&lt;/code> verwendet, treten zwei Heap-Allokationen auf: eine für das Objekt selbst und eine für den Kontrollblock. Bei Verwendung von &lt;code>std::make_shared&lt;/code> kann ein einzelner großer Speicherblock, der beides umfasst, in einer einzigen Heap-Allokation reserviert werden, was auch die Cache-Effizienz verbessert.&lt;/li>
&lt;li>&lt;strong>Ausnahmesicherheit (Exception Safety)&lt;/strong>:
In Standards vor C++17 war die Auswertungsreihenfolge von Funktionsargumenten nicht festgelegt. Wenn eine Ausnahme bei der Auswertung eines anderen Arguments auftrat, bevor der mit &lt;code>new&lt;/code> reservierte Zeiger an den Konstruktor von &lt;code>shared_ptr&lt;/code> übergeben wurde, bestand die Gefahr eines Speicherlecks. &lt;code>make_shared&lt;/code> vermeidet dieses Problem vollständig.&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">// Zu vermeidende Schreibweise (2 Speicherallokationen)
&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">// Empfohlene Schreibweise (1 Speicherallokation)
&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-auflösung-von-zirkelbezügen-und-überwachung">4. &lt;code>std::weak_ptr&lt;/code>: Auflösung von Zirkelbezügen und Überwachung
&lt;/h2>&lt;p>Das gemeinsame Eigentum hat eine fatale Schwachstelle: &amp;ldquo;Zirkelbezüge&amp;rdquo; (Circular References). Wenn Objekt A und Objekt B mit &lt;code>shared_ptr&lt;/code> gegenseitig aufeinander verweisen, bleibt der Strong Count für beide mindestens bei 1, wird bis zum Ende des Programms niemals 0 und es kommt zu einem Speicherleck.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Zirkelbezug (Speicherleck)"
A["Objekt A"] -->|"shared_ptr (Strong=1)"| B["Objekt B"]
B -->|"shared_ptr (Strong=1)"| A
end&lt;/div>
&lt;h3 id="41-durchbrechen-des-zirkelbezugs-mit-stdweak_ptr">4.1 Durchbrechen des Zirkelbezugs mit &lt;code>std::weak_ptr&lt;/code>
&lt;/h3>&lt;p>Die Lösung für dieses Problem ist &lt;code>std::weak_ptr&lt;/code>. Ein &lt;code>weak_ptr&lt;/code> wird aus einem &lt;code>shared_ptr&lt;/code> erstellt und verweist auf das Objekt, &lt;strong>erhöht aber nicht den Strong Count&lt;/strong>. Stattdessen wird der Weak Count erhöht. Dadurch kann man das Objekt &amp;ldquo;überwachen&amp;rdquo;, ohne Eigentum daran zu besitzen.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Durchbrechen des Zirkelbezugs"
C["Objekt A"] -->|"shared_ptr (Strong=1)"| D["Objekt B"]
D -.->|"weak_ptr (Weak=1)"| C
end&lt;/div>
&lt;h3 id="42-sicherer-zugriff-über-die-lock-methode">4.2 Sicherer Zugriff über die &lt;code>lock()&lt;/code>-Methode
&lt;/h3>&lt;p>&lt;code>weak_ptr&lt;/code> hat keine Operatoren für den direkten Zugriff auf das Objekt (&lt;code>-&amp;gt;&lt;/code> oder &lt;code>*&lt;/code>). Der Grund dafür ist, dass das Zielobjekt möglicherweise bereits zerstört wurde. Um sicher darauf zuzugreifen, ruft man die Methode &lt;code>lock()&lt;/code> auf, um temporär einen &lt;code>shared_ptr&lt;/code> zu erhalten.&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 verwenden, um Zirkelbezüge zu vermeiden
&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 von weak_ptr abrufen und zugreifen
&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 und nodeB werden ordnungsgemäß zerstört
&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-einschränkungen-des-gemeinsamen-eigentums-in-multithreading-umgebungen">5. Einschränkungen des gemeinsamen Eigentums in Multithreading-Umgebungen
&lt;/h2>&lt;p>Die Threadsicherheit von &lt;code>shared_ptr&lt;/code> wird oft missverstanden. Zwar gilt: &amp;ldquo;Die Aktualisierung der Referenzzählung im Kontrollblock ist threadsicher&amp;rdquo;, aber &amp;ldquo;Das Lesen und Schreiben des &lt;code>shared_ptr&lt;/code>-Objekts selbst ist nicht threadsicher&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Sichere Operationen&lt;/strong>: Mehrere Threads lesen und schreiben &lt;em>jeweils ihre eigenen&lt;/em> &lt;code>shared_ptr&lt;/code>-Instanzen (die jedoch denselben Kontrollblock teilen).&lt;/li>
&lt;li>&lt;strong>Data Race (Gefährlich)&lt;/strong>: Mehrere Threads lesen und schreiben gleichzeitig in &lt;em>genau dieselbe&lt;/em> &lt;code>shared_ptr&lt;/code>-Instanz.&lt;/li>
&lt;/ul>
&lt;p>Wenn dieselbe Instanz von mehreren Threads gemeinsam genutzt werden muss, muss &lt;code>std::atomic&amp;lt;std::shared_ptr&amp;lt;T&amp;gt;&amp;gt;&lt;/code> (C++20) verwendet werden oder sie muss durch einen Mutex (&lt;code>std::mutex&lt;/code>) geschützt werden.&lt;/p>
&lt;h2 id="6-mathematische-formulierung-der-referenzzählung">6. Mathematische Formulierung der Referenzzählung
&lt;/h2>&lt;p>Die Zustandsübergänge des Lebenszyklus im Kontrollblock lassen sich mathematisch wie folgt ausdrücken.
Sei $S(t)$ der Strong Count und $W(t)$ der Weak Count zum Zeitpunkt $t$.&lt;/p>
&lt;p>Anfangszustand (unmittelbar nach &lt;code>make_shared&lt;/code>):
&lt;/p>
$$ S(0) = 1, \quad W(0) = 0 $$
&lt;p>Wenn eine Kopie (Duplikation von &lt;code>shared_ptr&lt;/code>) erfolgt:
&lt;/p>
$$ S(t_{next}) = S(t) + 1 $$
&lt;p>Bedingung, unter der das verwaltete Objekt (Managed Object) zerstört wird:
&lt;/p>
$$ \lim_{t \to t_d} S(t) = 0 $$
&lt;p>Bedingung, unter der der Kontrollblock (Control Block) selbst aus dem Speicher freigegeben wird:
&lt;/p>
$$ S(t) = 0 \quad \land \quad W(t) = 0 $$
&lt;p>
Das heißt:
&lt;/p>
$$ S(t) + W(t) = 0 $$
&lt;p>Wie diese Formeln zeigen, bleibt der kleine Speicherplatz für den Kontrollblock weiterhin reserviert, solange &lt;code>weak_ptr&lt;/code> existieren ($W(t) > 0$), auch wenn das verwaltete Objekt bereits zerstört wurde. Dies kann in manchen Fällen der einzige Nachteil von &lt;code>make_shared&lt;/code> sein (da der Speicher des verwalteten Objekts und der Kontrollblock integriert sind, wird auch der riesige Speicherplatz für das verwaltete Objekt nicht an das System zurückgegeben, solange schwache Referenzen verbleiben). In der Regel überwiegen jedoch die Leistungsvorteile von &lt;code>make_shared&lt;/code> bei Weitem.&lt;/p>
&lt;h2 id="7-fazit">7. Fazit
&lt;/h2>&lt;p>Bei der Speicherverwaltung in Modern C++ ist das manuelle Verwalten von &lt;code>new&lt;/code>/&lt;code>delete&lt;/code> nicht mehr zeitgemäß.&lt;/p>
&lt;ol>
&lt;li>Standardmäßig sollte immer &lt;strong>&lt;code>std::unique_ptr&lt;/code>&lt;/strong> verwendet werden, um von Zero-Overhead zu profitieren und klares Eigentum in das Design zu integrieren.&lt;/li>
&lt;li>&lt;strong>&lt;code>std::shared_ptr&lt;/code>&lt;/strong> sollte nur dann verwendet werden, wenn der Lebenszyklus wirklich zwischen mehreren Eigentümern geteilt werden muss. Für die Erstellung sollte &lt;code>std::make_shared&lt;/code> verwendet werden.&lt;/li>
&lt;li>Bei der Implementierung von Datenstrukturen, in denen gemeinsame Kreise (Zirkelbezüge) auftreten können, oder bei Observer-Mustern sollte &lt;strong>&lt;code>std::weak_ptr&lt;/code>&lt;/strong> eingesetzt werden, um Speicherlecks proaktiv zu verhindern.&lt;/li>
&lt;/ol>
&lt;p>Ein tiefes Verständnis von Smart Pointern und deren gezielter Einsatz an den richtigen Stellen ermöglicht den Aufbau einer sicheren und robusten Softwarearchitektur, ohne die Leistung von C++ auch nur im Geringsten zu opfern.&lt;/p></description></item></channel></rss>