<?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/ko/tags/smart-pointers/</link><description>Recent content in Smart Pointers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/smart-pointers/index.xml" rel="self" type="application/rss+xml"/><item><title>메모리 누수를 방지하는 스마트 포인터(std::unique_ptr / shared_ptr) 활용법</title><link>http://kenji.blog/ko/p/cpp-smart-pointers-guide-unique-shared-ptr/</link><pubDate>Sat, 12 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/ko/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 pointer), 이중 해제(double free)와 같은 심각한 버그를 일으키는 온상이었습니다. 하지만 Modern C++(C++11 이후)의 등장으로 상황은 극적으로 변했습니다. 그 핵심을 이루는 것이 바로 &amp;lsquo;스마트 포인터(Smart Pointers)&amp;lsquo;입니다.&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>가 생략되어 메모리 누수가 발생합니다. 이를 방지하기 위한 패러다임이 &amp;lsquo;RAII(Resource Acquisition Is Initialization)&amp;lsquo;입니다. 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>는 동적으로 할당된 객체에 대해 &amp;lsquo;배타적 소유권(Exclusive Ownership)&amp;lsquo;을 가지는 스마트 포인터입니다. 특정 리소스를 소유할 수 있는 &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>의 가장 큰 매력은 그 성능입니다. 커스텀 딜리터(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>로 옮기려면 &lt;code>std::move&lt;/code>를 사용하여 이동 의미론(Move Semantics)을 활용합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Resource&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Resource&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Resource acquired&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Resource destroyed&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">do_something&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Doing something&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">process_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ptr&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">do_something&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 스코프를 벗어나면 ptr이 파괴되고, Resource도 해제됨
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">my_ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Resource&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// process_resource(my_ptr); // 에러: 복사 불가
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">process_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// 소유권 이동
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;my_ptr is now empty.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>다음 Mermaid 다이어그램은 &lt;code>std::move&lt;/code>에 의한 소유권 이동의 개념을 보여줍니다.&lt;/p>
&lt;div class="mermaid">graph LR
subgraph "std::move 이전"
A["unique_ptr (ptr1)"] -->|"소유함"| B["힙 메모리 (객체)"]
end
subgraph "std::move 이후"
C["unique_ptr (ptr1)"] -.->|"비어 있음 (nullptr)"| D["nullptr"]
E["unique_ptr (ptr2)"] -->|"소유함"| F["힙 메모리 (객체)"]
end&lt;/div>
&lt;h3 id="23-커스텀-딜리터-구현">2.3 커스텀 딜리터 구현
&lt;/h3>&lt;p>C 언어의 레거시 API(예: &lt;code>FILE*&lt;/code>이나 소켓 등)를 래핑할 때, 메모리 해제를 위해 &lt;code>delete&lt;/code> 이외의 함수(&lt;code>fclose&lt;/code> 등)를 호출해야 할 필요가 있습니다. &lt;code>std::unique_ptr&lt;/code>는 두 번째 템플릿 인자로 커스텀 딜리터를 지정할 수 있습니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdio&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 커스텀 딜리터용 함수 객체(Functor)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">FileDeleter&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">operator&lt;/span>&lt;span class="p">()(&lt;/span>&lt;span class="n">FILE&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">fp&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fp&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Closing file.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fp&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="n">UniqueFile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">FILE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">FileDeleter&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">UniqueFile&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fputs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello, Smart Pointers!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 스코프 종료 시 FileDeleter가 호출되어 fclose 됨
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>커스텀 딜리터로 함수 포인터나 람다 표현식을 사용하면 &lt;code>unique_ptr&lt;/code>의 크기가 증가할 가능성이 있지만, 위와 같이 상태가 없는 함수 객체(Functor)를 사용하면 C++의 &lt;strong>EBCO(Empty Base Class Optimization)&lt;/strong> 또는 C++20의 &lt;code>[[no_unique_address]]&lt;/code> 덕분에 크기가 원시 포인터에서 증가하지 않습니다(제로 오버헤드가 유지됩니다).&lt;/p>
&lt;h2 id="3-stdshared_ptr-공유-소유권과-컨트롤-블록">3. &lt;code>std::shared_ptr&lt;/code>: 공유 소유권과 컨트롤 블록
&lt;/h2>&lt;p>&lt;code>std::shared_ptr&lt;/code>는 여러 포인터가 동일한 객체를 공유하여 소유하기 위한 스마트 포인터입니다. 마지막 &lt;code>shared_ptr&lt;/code>가 파괴될 때 관리하고 있는 객체가 해제됩니다.&lt;/p>
&lt;h3 id="31-내부-아키텍처-컨트롤-블록">3.1 내부 아키텍처: 컨트롤 블록
&lt;/h3>&lt;p>&lt;code>std::shared_ptr&lt;/code>는 관리 대상 객체에 대한 포인터와는 별도로 **컨트롤 블록(Control Block)**이라고 불리는 메타데이터를 힙 상에 할당하여 공유합니다. 컨트롤 블록에는 다음 정보가 포함됩니다:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Strong Count (강한 참조 카운트)&lt;/strong>: 객체를 소유하고 있는 &lt;code>shared_ptr&lt;/code>의 수입니다. 이 값이 0이 되면 객체가 파괴됩니다.&lt;/li>
&lt;li>&lt;strong>Weak Count (약한 참조 카운트)&lt;/strong>: 객체를 감시하고 있는 &lt;code>weak_ptr&lt;/code>의 수입니다. Strong Count와 Weak Count가 모두 0이 되면 컨트롤 블록 자체가 해제됩니다.&lt;/li>
&lt;li>&lt;strong>커스텀 딜리터와 할당자&lt;/strong> (지정된 경우).&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph TD
A["std::shared_ptr&lt;T> (sp1)"] -->|"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> 객체 자체의 크기는 보통 원시 포인터의 2배(객체에 대한 포인터와 컨트롤 블록에 대한 포인터)가 됩니다.&lt;/p>
&lt;h3 id="32-성능과-원자적-연산">3.2 성능과 원자적 연산
&lt;/h3>&lt;p>컨트롤 블록 내의 참조 카운트는 멀티스레드 환경에서도 안전하게 증감할 수 있도록 **원자적 연산(Atomic Operations)**으로 구현되어 있습니다.&lt;/p>
&lt;p>x86/x64 아키텍처에서는 참조 카운트의 증감에 &lt;code>lock xadd&lt;/code>와 같은 원자적 명령이 사용됩니다. 이는 일반적인 정수 덧셈에 비해 수십 사이클의 오버헤드를 수반합니다. 따라서 값 전달(pass-by-value)로 &lt;code>shared_ptr&lt;/code>를 함수에 전달하면, 복사할 때마다 원자적인 증가(increment)와 감소(decrement)가 발생하여 성능이 저하됩니다.&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>(const 참조)로 전달하거나, 원시 포인터/참조를 전달해야 합니다.&lt;/p>
&lt;h3 id="33-stdmake_shared-vs-new">3.3 &lt;code>std::make_shared&lt;/code> vs &lt;code>new&lt;/code>
&lt;/h3>&lt;p>&lt;code>shared_ptr&lt;/code>를 생성할 때는 가능한 한 &lt;code>std::make_shared&lt;/code>를 사용해야 합니다. 여기에는 두 가지 중요한 이유가 있습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>메모리 할당 최적화&lt;/strong>:
&lt;code>new&lt;/code>를 사용하면 객체 본체의 할당과 컨트롤 블록의 할당이라는 2번의 힙 할당(heap allocation)이 발생합니다. &lt;code>std::make_shared&lt;/code>를 사용하면 두 가지를 포함하는 하나의 큰 메모리 블록을 1번의 힙 할당으로 확보할 수 있어 캐시 효율도 향상됩니다.&lt;/li>
&lt;li>&lt;strong>예외 안전성&lt;/strong>:
C++17 이전 규격에서는 함수 인자의 평가 순서가 미정이었기 때문에, &lt;code>new&lt;/code>로 확보한 포인터를 &lt;code>shared_ptr&lt;/code>의 생성자에 전달하기 전에 다른 인자를 평가하는 과정에서 예외가 발생하면 메모리 누수 위험이 있었습니다. &lt;code>make_shared&lt;/code>는 이 문제를 완전히 회피합니다.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 피해야 할 작성법 (2번의 메모리 할당)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 권장하는 작성법 (1번의 메모리 할당)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="4-stdweak_ptr-순환-참조-해결-및-감시">4. &lt;code>std::weak_ptr&lt;/code>: 순환 참조 해결 및 감시
&lt;/h2>&lt;p>공유 소유권에는 &amp;lsquo;순환 참조(Circular References)&amp;lsquo;라는 치명적인 약점이 있습니다. 객체 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를 증가시킵니다. 이를 통해 소유권을 가지지 않고 객체를 &amp;lsquo;감시&amp;rsquo;할 수 있습니다.&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>lock()&lt;/code> 메서드를 호출하여 일시적으로 &lt;code>shared_ptr&lt;/code>를 얻어야 합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Node&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">next&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">weak_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">prev&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 순환 참조를 방지하기 위해 weak_ptr 사용
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Created &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Destroyed &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">nodeA&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;A&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">nodeB&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nodeA&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">next&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeB&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nodeB&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">prev&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeA&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// weak_ptr에서 shared_ptr를 얻어 접근
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span> &lt;span class="n">locked_prev&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nodeB&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">prev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Node B&amp;#39;s prev is &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">locked_prev&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Node B&amp;#39;s prev is already destroyed.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// nodeA와 nodeB는 적절히 파괴됨
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-멀티스레드-환경에서의-공유-소유권-제약">5. 멀티스레드 환경에서의 공유 소유권 제약
&lt;/h2>&lt;p>&lt;code>shared_ptr&lt;/code>의 스레드 안전성(Thread Safety)에 대해서는 오해하기 쉽습니다. &amp;ldquo;컨트롤 블록 내의 참조 카운트 갱신은 스레드 안전(Thread Safe)&amp;ldquo;하지만, &amp;ldquo;&lt;code>shared_ptr&lt;/code> 객체 자체의 읽고 쓰기는 스레드 안전하지 않습니다&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>안전한 조작&lt;/strong>: 여러 스레드가 &lt;em>각자 자신의&lt;/em> &lt;code>shared_ptr&lt;/code> 인스턴스(단, 동일한 컨트롤 블록을 공유함)를 읽고 쓰는 것.&lt;/li>
&lt;li>&lt;strong>데이터 레이스(위험)&lt;/strong>: 여러 스레드가 &lt;em>완전히 동일한&lt;/em> &lt;code>shared_ptr&lt;/code> 인스턴스에 대해 동시에 읽고 쓰는 것.&lt;/li>
&lt;/ul>
&lt;p>동일한 인스턴스를 여러 스레드에서 공유해야 할 경우에는 &lt;code>std::atomic&amp;lt;std::shared_ptr&amp;lt;T&amp;gt;&amp;gt;&lt;/code>(C++20)를 사용하거나, 뮤텍스(&lt;code>std::mutex&lt;/code>)로 보호해야 합니다.&lt;/p>
&lt;h2 id="6-참조-카운트의-수학적-공식화">6. 참조 카운트의 수학적 공식화
&lt;/h2>&lt;p>컨트롤 블록에서의 라이프사이클 상태 전이를 수학적으로 표현하면 다음과 같습니다.
시간 $t$에서의 Strong Count를 $S(t)$, Weak Count를 $W(t)$라고 합니다.&lt;/p>
&lt;p>초기 상태(&lt;code>make_shared&lt;/code> 직후):
&lt;/p>
$$ S(0) = 1, \quad W(0) = 0 $$
&lt;p>복사(&lt;code>shared_ptr&lt;/code>의 복제)가 이루어지면:
&lt;/p>
$$ S(t_{next}) = S(t) + 1 $$
&lt;p>관리 객체(Managed Object)가 파괴되는 조건:
&lt;/p>
$$ \lim_{t \to t_d} S(t) = 0 $$
&lt;p>컨트롤 블록(Control Block) 자체가 메모리에서 해제되는 조건:
&lt;/p>
$$ S(t) = 0 \quad \land \quad W(t) = 0 $$
&lt;p>
즉,
&lt;/p>
$$ S(t) + W(t) = 0 $$
&lt;p>이 수식이 보여주듯, &lt;code>weak_ptr&lt;/code>가 계속 존재하는 한($W(t) > 0$), 관리 객체가 파괴되었더라도 컨트롤 블록을 위한 작은 메모리 공간은 계속 확보되어 있습니다. 이것이 &lt;code>make_shared&lt;/code>의 유일한 단점(관리 객체의 메모리와 컨트롤 블록이 일체화되어 있기 때문에, 약한 참조가 남아 있으면 관리 객체용 거대한 메모리 공간도 시스템에 반환되지 않음)이 되는 경우가 있지만, 보통은 &lt;code>make_shared&lt;/code>의 성능상 이점이 압도적으로 큽니다.&lt;/p>
&lt;h2 id="7-결론">7. 결론
&lt;/h2>&lt;p>Modern C++에서의 메모리 관리는 더 이상 수동으로 &lt;code>new&lt;/code>/&lt;code>delete&lt;/code>를 관리하는 시대가 아닙니다.&lt;/p>
&lt;ol>
&lt;li>기본적으로는 항상 **&lt;code>std::unique_ptr&lt;/code>**를 사용하고, 제로 오버헤드의 혜택을 누리면서 명확한 소유권을 설계에 포함시킵니다.&lt;/li>
&lt;li>정말로 여러 소유자 간에 라이프사이클을 공유해야 할 필요가 있는 경우에만 **&lt;code>std::shared_ptr&lt;/code>**를 사용하며, 생성에는 &lt;code>std::make_shared&lt;/code>를 사용합니다.&lt;/li>
&lt;li>공유의 고리(순환 참조)가 발생할 수 있는 데이터 구조나 옵저버 패턴(Observer Pattern)의 구현에는 **&lt;code>std::weak_ptr&lt;/code>**를 활용하여 메모리 누수를 미연에 방지합니다.&lt;/li>
&lt;/ol>
&lt;p>스마트 포인터를 깊이 이해하고 적재적소에 활용함으로써, C++의 성능을 전혀 희생하지 않고 안전하고 견고한 소프트웨어 아키텍처를 구축할 수 있게 됩니다.&lt;/p></description></item></channel></rss>