<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>whale-park18.log</title>
        <link>https://velog.io/</link>
        <description>C++, C#</description>
        <lastBuildDate>Sun, 03 Sep 2023 16:52:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>whale-park18.log</title>
            <url>https://velog.velcdn.com/images/whale-park18/profile/6ee90be4-0541-4fe5-9e24-6dae60999b23/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. whale-park18.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/whale-park18" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[C++ Korea] 2023 Meet-Up(8월)]]></title>
            <link>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up8%EC%9B%94</link>
            <guid>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up8%EC%9B%94</guid>
            <pubDate>Sun, 03 Sep 2023 16:52:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/whale-park18/post/557f1196-3102-4810-a442-cb09d379739d/image.jpg" alt="[C++ Korea] 2023 Meet-Up(8월)"></p>
<h1 id="onnx-runtime으로-시작하는-c-머신러닝">ONNX Runtime으로 시작하는 C++ 머신러닝</h1>
<p>발표자: 박문식(이스트소프트 유틸리티 개발팀)</p>
<p><strong>기존의 머신러닝 모델 개발 및 배포 환경</strong></p>
<ul>
<li>고성능 서버 머신</li>
<li>Python</li>
<li>GPU 가속 사용</li>
<li>PyTorch, Tensorflow 등의 머신러닝 프레임워크</li>
</ul>
<p><strong>왜? 이렇게 정형화 되었는가?</strong></p>
<ul>
<li>예전에는 CPU를 사용</li>
<li>때문에 연산 처리에 한계가 존재</li>
<li>GPU의 등장으로 상황이 변하게 됨<ul>
<li>간단한 연산들을 병렬적으로 계산</li>
<li>쿠다가 없었을 때, 그래픽 API만 노출되었기 때문에 쉐이더로 계산함</li>
</ul>
</li>
</ul>
<p><strong>현재 하나의 패러다임이 추가됨</strong></p>
<ul>
<li>(ML Model) Interface at the edge</li>
<li>기술이 발전함에 따라 머신러닝 모델들의 연산 요구량은 낮아졌고 정확도는 증가됨</li>
<li>엣지 디바이스의 연산 능력 향상, 전력 낮아짐</li>
<li>엣지 디바이스에서 모델을 돌리면 응답 속도, 보안성 강화</li>
<li>때문에 고성능 서버에서만 돌아가는 시절은 아니다. 클라이언트에서도 사용할 수 있게 됨</li>
</ul>
<p>** 엣지 디바이스 특성**</p>
<ul>
<li>우리가 관리하는 디바이스가 아니기 때문에 호환성 문제가 발생<ul>
<li>CPU, GPU, 메모리 속도와 용량 등을 다 따져봐야 함</li>
<li>휴대용 엣지 디바이스의 경우, 배터리 문제가 추가됨</li>
<li>결국 연산 요구량이 적은 머신러닝 모델을 돌려야함</li>
</ul>
</li>
</ul>
<p><strong>C++ 머신러닝 플랫폼</strong></p>
<ul>
<li>libtorch<ul>
<li>PyTorch 모델만 돌릴 수 있음</li>
<li>바이너리 사이즈가 큼</li>
<li>실제 모델에서 사용하기에는 아직 무리</li>
</ul>
</li>
<li>ggml<ul>
<li>OpenAI Whisper(Speech to Text) -&gt; whisper.cpp</li>
<li>Meta LLaMA -&gt; llam.cpp</li>
<li>CPU에서 잘 돌아갈 수 있음(단, GPU만큼의 성능이 나온다고 볼 수 없음)</li>
<li>유명 모델만 최적화가 들어가기 때문에 개발자가 추가적으로 최적화를 해야함</li>
</ul>
</li>
</ul>
<p><strong>ONNX(Open Neural Network Excange)</strong></p>
<ul>
<li>ONNX<ul>
<li>MS에서 만듬</li>
<li>.NET과 Java와 같이 중간 언어라는 개념을 도입해서 만든 머신러닝 모델을 표준화하기 위해 제안된 규격</li>
<li>MS에서 만들었기 때문에 Windows 지원이 뛰어남</li>
<li>iOS, macOS, Linux, Android, Web도 지원</li>
<li>많은 하드웨어 가속을 지원</li>
<li>오픈 소스</li>
</ul>
</li>
<li>특징<ul>
<li>플랫폼에 종속적이지 않음</li>
<li>환경에 종속적이지 않아 ONNX Runtime이 지원하는 모든 환경에서 ONNX로 만들어진 모델을 돌릴 수 있음</li>
<li>런타임 바이너리 크기가 작음 = 쉽게 배포 가능</li>
<li>특정 연산에 대해 특정 환경에서 제공하는 다양한 최적화 모델 수정 없이 활용할 수 있음</li>
</ul>
</li>
</ul>
<p><strong>실습</strong></p>
<ul>
<li>github: microsoft/onnxruntime</li>
<li><code>MNIST</code>: ONNX에서 사용되는 머신러닝 필수 구조체</li>
<li><code>tensor</code>: ML에서 사용되는 추상화된? 행렬</li>
</ul>
<p><strong>QnA</strong></p>
<pre><code>Q1. 디바이스의 CPU에서만 돌아가는 것을 전제로 깔았는가?

A1. GPU는 환경 문제가 크기 때문에 CPU를 전제로 깔았다.</code></pre><pre><code>Q2. PyTorch, Tensorflow를 ONXX로 변경하기 위해서 따로 공부해야 하는가?

A2. ONNX에서 ONNX로 변경하는 기능을 제공하므로 굳이 공부할 필요는 없다. 
단, ONNX에서 아직 제공하지 못하는 일부 기능들이 있기 때문에 완벽하게 변경된다고 확신할 수 없다.</code></pre><h1 id="d3d12-mesh-shader-소개">D3D12 Mesh Shader 소개</h1>
<p>발표자: 유영천</p>
<h1 id="프로젝트의-운명을-좌우하는-레서기-코드">프로젝트의 운명을 좌우하는 레서기 코드</h1>
<p>발표자: 정민우</p>
<p><strong>DBMS의 특징</strong></p>
<ul>
<li>매우 긴 소프트웨어의 수명</li>
<li>많은 개발자들이 참여</li>
<li>많은 개발자들이 퇴사...</li>
<li>고객사들의 변화에 매우 민감</li>
<li>회사의 지출 대부분이 유지 보수</li>
</ul>
<p><strong>레거시 코드란?</strong></p>
<ul>
<li>테스트 코드가 없는 코드</li>
<li>바꾸기 힘든 코드</li>
<li>정체를 알아볼 수 없는 코드</li>
<li><blockquote>
<p>함부로 건들이지 못함</p>
</blockquote>
</li>
</ul>
<p><strong>레거시 코드</strong>
Naming Issues</p>
<ul>
<li>예시<ul>
<li>무책임한 이름</li>
<li>약어의 남용<ul>
<li><code>MInfo</code></li>
<li>이름만으로는 어떤 역할인지 알 수가 없음...</li>
<li>정답은 메인 정보를 담는 변수</li>
</ul>
</li>
<li>너무 일반적인 이름<ul>
<li>너무 범용적인 이름이라면 오히려 정확히 어떤 역할을 하는지 알 수 없음</li>
<li>서로 구분하기 힘든 이름<ul>
<li>암시적인 규칙에 의한 함수 이름과 비슷한 작동을 하는 이름의 함수</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>좋지 않은 이름의 피해<ul>
<li>코드 구성 요소간의 역할 구분이 어려움</li>
<li>코드를 이해하는 시간 증가</li>
</ul>
</li>
<li>해결책<ul>
<li>이름 짓는 것에 조금 더 성의 있게...</li>
<li>동료와 충분한 커뮤니케이션<ul>
<li>성의 있는 커밋 메시지</li>
<li>코드 리뷰 시간</li>
<li>문서화</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>함수</p>
<ul>
<li>예시<ul>
<li>너무 많은 파라미터<ul>
<li>비슷한 동작을 하는 A, B 상황</li>
<li>플래그를 이용해서 분기 적용</li>
</ul>
</li>
<li>전역 변수 사용<ul>
<li>유지.보수에서 최악</li>
<li>함수 재사용이 까다로움</li>
<li>함수를 바꾸기 어려워짐</li>
<li>디버깅이 힘들어짐</li>
<li>컴파일 시간 증가</li>
</ul>
</li>
</ul>
</li>
<li>해결책<ul>
<li>너무 많은 파라미터</li>
<li>흠..</li>
<li>당장의 쉬운 구현은 절대 정답이 아님</li>
<li>이해하기 쉬운 함수를 작성했는지 항상 생각해야 함</li>
<li>테스트 코드</li>
<li>코드 리뷰 시간</li>
</ul>
</li>
</ul>
<p>지식의 중복</p>
<ul>
<li>지식의 중복: 하나의 변수를 변경했을 때, 다른 변수까지 변경해야하는 것</li>
<li>왜 문제인가?<ul>
<li>문제 발생시, 디버깅이 너무 어려워짐</li>
<li>여러 데이터간 정합성이 맞지 않아 에러...</li>
</ul>
</li>
<li>해결책<ul>
<li>Attribute를 Getter로 대체</li>
</ul>
</li>
<li>교훈<ul>
<li>데이터 간 지식의 중복은 무조건 잊혀지기 마련</li>
<li>지식의 중복은 반드시 피하거나 감춰야 함</li>
</ul>
</li>
</ul>
<p>직접 겪은 레거시 코드...</p>
<ul>
<li>막대한 레거시 코드</li>
<li>고객사가 증가할 수록 유지.보수 비용 증가</li>
<li>95% 이상의 종사자들의 업무가 유지.보수</li>
<li>발전이 정체된 소프트웨어</li>
<li>업무 만족도 저하 -&gt; 잦은 퇴사 -&gt; 기존의 직원 업무 질 저하 -&gt; 반복</li>
</ul>
<p>해결책</p>
<ul>
<li>바꾸기 쉬운 코드인지 항상 생각하자</li>
<li>좋은 개발 문화<ul>
<li>테스트 코드</li>
<li>코드 리뷰</li>
<li>문서화</li>
<li>활발한 커뮤니케이션</li>
</ul>
</li>
<li>주기적인 리팩토링<ul>
<li>기업 입장에서는 돈이 되지 않지만 유지.보수에는 매우 좋음</li>
</ul>
</li>
</ul>
<p>QnA</p>
<pre><code>Q1. 가장 길었던 함수나 파일

A1. gdd를 많이 사용, 만줄이 넘어가는 파일 존재, 조금만 만져도 컴파일 시간이 너무 늘어난다.</code></pre><pre><code>Q2.
* 패키지 형태로 오랫동안 유지.보수하는 회사가 적은거 같다.
* 함수명, 변수명을 잘했으면 좋겠다고 생각을하지만 그러면 이름이 너무 길어진다.
* 문서화하는 것은 좋다 하지만, 현실의 괴리감이 자주 발생하게 된다(코드와 문서와 동기화 안됨).

A2-1.
* 너무 긴 것도 않좋다고 생각한다. 그래서 코드 리뷰를 통해 적절한 이름을 찾는 것도 한 방법이라 생각한다
* 모든 코드의 문서화 보단 핵심 부분(약어 규칙, 명명 규칙 등)을 정해, 다른 문서가 필요 없게...

A2-2. 다른 분의 답변
* 변수/함수명이 길어졌다면 코드 리팩토링을 해야 할 신호라고 생각
* 주석, 커밋 메시지를 현재와 미래의 팀원 뿐만 아니라 미래의 내가 봤을 때도 알아 볼 수 있게 작성해야 한다(경험담...).</code></pre><pre><code>Q3. 특별한 개발 문호가 존재하는가?

A3. 특출난 것은 없고 코드 리뷰 시간을 최대한 갖으려고 한다. 하지만 윗 분들은 잘 이해하지 못해서 아쉽다. 
책은 이상적이지만 그래도 배울 것이 많기 때문에 많이 읽어보려고 한다.</code></pre><pre><code>Q4. 우리 회사 같은 경우, Wrapping을 하고 구분이 더 힘들어 졌다.

A1. 제대로 못들음...</code></pre><pre><code>Q5. 코드 리뷰, 현업이 궁금하다. 참석자 모두가 리뷰하는가? 또는 몇 명만 하는가?

A5. 코드에 따라 다르다. 간단한 것은 몇 명만 집중적으로, 핵심적인 코드는 참석자 모두가 인지해야하기 때문에 모두 참여한다.
우리는 코드 리뷰 시간을 따로 할당해서 정식적으로 부정적인 생각 없이 참여하게 만들었다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] weak_ptr]]></title>
            <link>https://velog.io/@whale-park18/C-weakptr</link>
            <guid>https://velog.io/@whale-park18/C-weakptr</guid>
            <pubDate>Wed, 30 Aug 2023 07:53:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 글은 <code>std::weak_ptr</code>의 소개와 간단한 사용법을 중심으로 설명합니다.</p>
<p><code>std::weak_ptr</code> 클래스가 갖는 멤버 변수나 함수에 대한 자세한 내용은 시간이 되면 나중에 작성하겠습니다.</p>
</blockquote>
<h1 id="설명">설명</h1>
<h2 id="stdweak_ptr란"><code>std::weak_ptr</code>란?</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/4e68fbf9-032d-4cf2-8b23-43bd662d7107/image.jpg" alt="`std::weak_ptr 생성시, 제어 블록`"></p>
<p><code>std::weak_ptr</code>는 <code>std::shared_ptr</code>에 의해 관리되는 자원 객체를 공유하지만 소유하지 않는 (<code>std::shared_ptr</code>의 약점(순환 참조)를 보강해주는)스마트 포인터입니다. 자원 객체를 소유하지 않기 때문에 <code>std::shared_ptr</code>로 관리되는 자원 객체를 가리켜도 참조 개수에 영향을 미치지 않습니다(대신 약한 개수(weak count)에 영향을 미칩니다).</p>
<p><code>std::weak_ptr</code>는 이름처럼 <strong>&quot;약한&quot;</strong> 포인터(자원 객체와의 관계가)이기 때문에 <code>std::unique_ptr</code>나 <code>std::shared_ptr</code>처럼 자체적으로 포인터로서의 역할을 수행할 수 없습니다.</p>
<p>또한 <code>std::unique_ptr</code>나 <code>std::shared_ptr</code>는 자신이 자원 객체를 관리하기 때문에 자원 객체가 살아있는지, 소멸했는지 알 수 있지만 <code>std::weak_ptr</code>는 자원 객체를 가리킬 뿐, 관리하지 않기 때문에 자신이 가리킨 자원 객체가 살아있는지 소멸했는지 알 수 없습니다. 하지만 <code>std::weak_ptr</code>는 <code>std::shared_ptr</code>와 같은 구조(자원 객체 + 제어 블록)이기 때문에 제어 블록을 통해 자원 객체가 살아있는지 소멸했는지 판단할 수 있습니다.</p>
<h2 id="사용법">사용법</h2>
<blockquote>
<p><code>std::weak_ptr</code>를 사용하기 위해선 <code>&lt;memory&gt;</code> 헤더를 포함해야 합니다.</p>
</blockquote>
<h3 id="생성자를-이용한-생성-및-초기화">생성자를 이용한 생성 및 초기화</h3>
<p><code>std::waek_ptr</code>는 위에서 언급했듯이 <code>std::shared_ptr</code>가 관리하는 자원 객체를 가리킵니다. 즉, 생성자, 복사 연산자, 복사 배정 연산자의 인자로 포인터 객체(<code>std::shared_ptr</code>, <code>std::weak_ptr</code>)를 넘겨주면 됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string m_name;
    int m_age;

public:
    Person(std::string name, int age) : m_name(name), m_age(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }
};


int main()
{
    std::shared_ptr&lt;Person&gt; sPtr = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    std::weak_ptr&lt;Person&gt; wPtr1(sPtr);
    std::weak_ptr&lt;Person&gt; wPtr2 = wPtr1;
    std::weak_ptr&lt;Person&gt; wPtr3;
    wPtr3 = wPtr2;

    std::cout &lt;&lt; &quot;sPtr 참조 개수: &quot; &lt;&lt; sPtr.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;wPtr1 참조 개수: &quot; &lt;&lt; wPtr1.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;wPtr2 참조 개수: &quot; &lt;&lt; wPtr2.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;wPtr3 참조 개수: &quot; &lt;&lt; wPtr3.use_count() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
sPtr 참조 개수: 1
wPtr1 참조 개수: 1
wPtr2 참조 개수: 1
wPtr3 참조 개수: 1
소멸자 호출</code></pre><p><code>std::weak_ptr</code>는 <code>std::shared_ptr</code>와 동일한 멤버 함수인 <code>use_count</code>를 갖고 있습니다. <code>std::weak_ptr</code>는 약한 개수에 관여하기 때문에 약한 개수의 수를 반환할 것 같지만 <code>std::shared_ptr</code>와 동일하게 참조 개수를 반환합니다.</p>
<h3 id="stdweak_ptr가-가리키는-자원-객체-사용하기"><code>std::weak_ptr</code>가 가리키는 자원 객체 사용하기</h3>
<p><code>std::weak_ptr</code>는 자원 객체를 가리킬 뿐, 역참조를 할 수 없습니다. 때문에 포인터 객체를 <code>std::shared_ptr</code>로 업그레이드해서 사용해야 합니다. 하지만 그 전에 반드시 현재 가리키고 있는 자원 객체가 소멸되었는지 확인해야 합니다.</p>
<p>이를 위한 멤버 함수 <code>expired</code>가 존재하지만 원자적으로 연산되지 않기 때문에 멀티 쓰레드 환경에서 잘못 사용할 경우 미정의 행동이 발생할 수 있습니다.</p>
<p>이런 문제를 해결할 수 있는 것이 <code>lock</code> 멤버 함수입니다 <code>lock</code> 멤버 함수는 원자적으로 연산되며 <code>std::weak_ptr</code>가 가리키는 자원 객체가 소멸했다면 아무 것도 가리키지 않는 <code>std::shared_ptr</code>를 반환하지만 존재한다면 <code>std::weak_ptr</code>와 동일한 자원 객체를 가리키는 <code>std::shared_ptr</code>를 반환합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string m_name;
    int m_age;
    std::weak_ptr&lt;Person&gt; m_wPtrFirend;

public:
    Person(std::string name, int age) : m_name(name), m_age(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    void SetFirend(std::shared_ptr&lt;Person&gt;&amp; sPtrFirend) { m_wPtrFirend = sPtrFirend; }

    const std::string GetName() { return m_name; }
    const int GetAge() { return m_age; }
    const std::weak_ptr&lt;Person&gt;&amp; GetFirend() { return m_wPtrFirend; }

    void ResetFirend()
    {
        m_wPtrFirend.reset();
    }

    void AccessFirend()
    {
        auto sPtrFirend = m_wPtrFirend.lock();

        if (sPtrFirend)
        {
            std::cout &lt;&lt; &quot;접근 성공, firend 정보 { &quot; &lt;&lt; sPtrFirend-&gt;GetName() &lt;&lt; &quot;, &quot; &lt;&lt; sPtrFirend-&gt;GetAge() &lt;&lt; &quot; }&quot;;
        }
        else
        {
            std::cout &lt;&lt; &quot;접근 실패(참조한 객체가 소멸됨)&quot; &lt;&lt; &#39;\n&#39;;
        }
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtrWhale = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    std::shared_ptr&lt;Person&gt; sPtrPark18 = std::make_shared&lt;Person&gt;(&quot;park18&quot;, 20);

    sPtrWhale-&gt;SetFirend(sPtrPark18);
    sPtrPark18-&gt;SetFirend(sPtrWhale);

    std::cout &lt;&lt; &quot;sPtrWhale 참조 개수: &quot; &lt;&lt; sPtrWhale.use_count() &lt;&lt; &#39;\n&#39;
              &lt;&lt; &quot;sPtrPark18 참조 개수: &quot; &lt;&lt; sPtrPark18.use_count() &lt;&lt; &#39;\n&#39;;

    std::cout &lt;&lt; std::boolalpha &lt;&lt; &quot;sPtrWhale의 firend 만료: &quot; &lt;&lt; sPtrWhale-&gt;GetFirend().expired() &lt;&lt; &#39;\n&#39;
                                &lt;&lt; &quot;sPtrPark18의 firend 만료: &quot; &lt;&lt; sPtrPark18-&gt;GetFirend().expired() &lt;&lt; &#39;\n&#39;;

    sPtrWhale-&gt;AccessFirend();
    sPtrWhale.reset();

    sPtrPark18-&gt;AccessFirend();
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
생성자 호출
sPtrWhale 참조 개수: 1
sPtrPark18 참조 개수: 1
sPtrWhale의 firend 만료: false
sPtrPark18의 firend 만료: false
접근 성공, firend 정보 { park18, 20 }소멸자 호출
접근 실패(참조한 객체가 소멸됨)
소멸자 호출</code></pre><h1 id="참고">참고</h1>
<p><a href="https://en.cppreference.com/w/cpp/memory/weak_ptr">cppreference - en</a></p>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/how-to-create-and-use-weak-ptr-instances?view=msvc-170">VisualC++ Docs</a></p>
<p><a href="https://www.tcpschool.com/cpp/cpp_template_smartPointer">TCP School</a></p>
<p><a href="https://modoocode.com/252">모두의 코드</a></p>
<p><a href="https://ansohxxn.github.io/cpp/chapter15-7/">식빵맘(ansohxxn)</a></p>
<p><a href="https://ozt88.tistory.com/29?category=118910">ozt88</a></p>
<p><a href="https://ebook.insightbook.co.kr/book/117">Effective Modern C++</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++ Korea] 2023 Meet-Up(06월)]]></title>
            <link>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up06%EC%9B%94</link>
            <guid>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up06%EC%9B%94</guid>
            <pubDate>Wed, 16 Aug 2023 13:55:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/whale-park18/post/185b3978-b056-4039-9f55-6b3468b4fa82/image.jpg" alt="[C++ Korea] 2023 Meet-Up(6월)"></p>
<h1 id="d3d-tiled-resources를-이용한-텍스처-스트리밍">D3D Tiled Resources를 이용한 텍스처 스트리밍</h1>
<p>발표자: 유영천</p>
<ul>
<li><p>맵의 텍스처링 방법</p>
<ul>
<li>넓은 맵의 베이스 텍스처를 사용 -&gt; 타일처럼 패턴이 보이게 됨.</li>
</ul>
</li>
<li><p>거대 텍스처 맵핑의 특성</p>
<ul>
<li>언제나 텍스처의 모든 영역이 한 번에 보여지는 것이 아님.</li>
<li>가까운 곳은 높은 해상도, 멀 수록 낮은 해상도</li>
</ul>
</li>
<li><p>가상 텍스처링</p>
<ul>
<li>가상 메모리..?</li>
<li>DX11 이상부터 지원</li>
<li>GPU에서 가상 메모리는 OS에서 관리하지 않기 때문에 프로그래머가 관리해야 함.</li>
<li>민맵?</li>
<li>Packed Mip</li>
<li>Tiled Pool<ul>
<li>페이지 관리</li>
<li>프로그래머의 몫<ul>
<li>예) 인덱스 발급기 이용(맵핑)</li>
<li>삭제 됐을 때, 메모리에 이가 빠지게 됨(배열).</li>
<li>이가 빠진 곳을 활용하지 않으면 메모리 낭비 -&gt; 효율적으로 관리하기 위해 인덱스로 관리</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>File로 부터 읽어 이미지 데이터 로드</p>
<ul>
<li>파일 헤더를 읽어 위치 확인</li>
<li>필요할 때, 헤더를 이용해 참조</li>
</ul>
</li>
<li><p>GPU 프로그래밍의 경우 로직에 따라 성능의 차이가 심함.</p>
</li>
</ul>
<h1 id="c을-버린-유저가-이야기-하는-c가-지향해야-하는-모습은">C++을 버린 유저가 이야기 하는 C++가 지향해야 하는 모습은?</h1>
<p>발표자: 김두훈</p>
<ul>
<li><p>왜 C++는 어려운가?</p>
<ul>
<li>너무 자유롭다 == 너무 높은 난이도<ul>
<li>템플릿</li>
<li>컴파일러 설정</li>
<li>코루틴</li>
</ul>
</li>
</ul>
</li>
<li><p>따라가기 힘든 표준</p>
<ul>
<li>Peprecated, Feature가 추가되는게 너무 극단적으로 많음<ul>
<li>시간이 지날 수록 추가되는 양이 많아짐...</li>
</ul>
</li>
</ul>
</li>
<li><p>극악의 방대함, 더딘 지원</p>
<ul>
<li>지속적인 발전으로 기능은 방대해졌지만 컴파일러 지원이 늦음</li>
</ul>
</li>
<li><p>그래서 지향해야 하는 것은?</p>
<ol>
<li><p>퍼포먼스와 엄밀함 보다는 &quot;사용성</p>
<ul>
<li>패키지에 대한 에코-시스템<ul>
<li>vcpkg</li>
<li>conan</li>
</ul>
</li>
</ul>
</li>
<li><p>사용자 친화적</p>
<ul>
<li>이제 하위 호환성을 놓아줄 때가 됐다(이미 망한거 같으니...).</li>
<li>한국에서 C++ 커뮤니티가 부족함.</li>
</ul>
</li>
</ol>
</li>
</ul>
<h1 id="클래스를-쓰지-않고-게임-개발하기-ecs를-통한-게임-개발">클래스를 쓰지 않고 게임 개발하기 (ECS를 통한 게임 개발)</h1>
<p>발표자: 옥찬호(<a href="https://github.com/utilForever">@utilForever</a>)</p>
<ul>
<li><p>ECS(<strong>E</strong>ntity <strong>C</strong>omponent <strong>S</strong>ystem)</p>
<ul>
<li><p>상태와 기능을 분리</p>
</li>
<li><p>구성 요소</p>
<ul>
<li>Entity(포인터)<ul>
<li>컴포넌트를 담아두는 곳</li>
<li>행동(기능)이나 데이터 X</li>
<li>어떤 데이터가 있는지 식별</li>
</ul>
</li>
<li>Component(실제 데이터)<ul>
<li>실제 데이터를 저장하는 곳</li>
<li>단순 태그, 값을 저장하는 컨테이너, 무언가를 참조하는 컨테이너 등</li>
</ul>
</li>
<li>System<ul>
<li>컴포넌트의 데이터를 현재 상태에서 다음 상태로 변환하는 논리</li>
<li>전체 인티티 중 특정 컴포넌트를 가리키는 엔티티만 필터링한 다음, 컴포넌트와 관련해 필요한 동작 수행<ul>
<li>데이터 변경</li>
<li>새로운 컴포넌트 추가</li>
<li>기존 컨테이너 삭제</li>
</ul>
</li>
</ul>
</li>
<li>왜 사용하는가?<ul>
<li>객체지향에 비해 <strong>성능</strong>이 뛰어나다.</li>
<li>데이터 지향 디자인</li>
<li>지역성(Locality) 개념<ul>
<li>캐시가 효율적으로 동작하려면, 캐시에 저장할 데이터가 지역성을 가져야 함.</li>
<li>지역성이란 데이터 접근이 시간적, 공간적으로 가깝게 일어나는 것.</li>
<li>공간 지약성<ul>
<li>특정 데이터와 가까운 주소가 순서대로 접근되었을 경우 공간적 지역성이라고 한다.</li>
</ul>
</li>
<li>객체지향의 경우 이 공간 지약성이 떨어진다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>ECS 라이브러리</p>
<ul>
<li>C++<ul>
<li>entt<ul>
<li>마인크래프트 및 마인크래프트 어스에서 사용</li>
</ul>
</li>
<li>entityX</li>
<li>Ffles</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>데모</p>
<ul>
<li><a href="https://github.com/utilForever/2021-CoMu-ECS-Development/tree/main">2021-CoMu-ECS-Development</a></li>
<li>엔티티에서는 컴포넌트가 어떤 것인지 구별할 수 없기 때문에 태그 컴포넌트를 이용해 탐색용으로 사용함</li>
</ul>
</li>
<li><p>정리</p>
<ul>
<li>ECS는 데이터 지향 디자인을 통해 특정 </li>
</ul>
</li>
</ul>
<h1 id="커뮤니티의-어제와-오늘">커뮤니티의 어제와 오늘</h1>
<p>발표자: 방기연(<a href="https://github.com/GiyeonBang">@giyeonbang</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++ Korea] 2023 Meet-Up(04월)]]></title>
            <link>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up04%EC%9B%94</link>
            <guid>https://velog.io/@whale-park18/C-Korea-2023-Meet-Up04%EC%9B%94</guid>
            <pubDate>Wed, 16 Aug 2023 13:54:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/whale-park18/post/a1af5a57-5cb6-4ae6-8c9c-2c836fe81495/image.jpg" alt="[C++ Korea] 2023 Meet-Up(04월)"></p>
<h1 id="roku-korea-소개-및-c-in-roku-korea-server">Roku Korea 소개 및 C++ in Roku Korea (Server)</h1>
<p>발표자: C++ in Roku Korea(Roku)
영상 링크: <a href="https://youtu.be/gv7LBEoox5k">C++ Korea Youtube</a></p>
<h1 id="roku-korea-소개-및-c-in-roku-korea-client">Roku Korea 소개 및 C++ in Roku Korea (Client)</h1>
<p>발표자: C++ in Roku Korea(Roku)
영상 링크: <a href="https://youtu.be/cOGSbZpNMEw">C++ Korea Youtube</a></p>
<h1 id="cuda-raytracing을-이용한-복셀오브젝트-가시성-검사">CUDA Raytracing을 이용한 복셀오브젝트 가시성 검사</h1>
<p>발표자: 유영천
영상 링크: <a href="https://youtu.be/17i5UUUQTLM">C++ Korea Youtube</a></p>
<h1 id="objective-c-runtime을-사용해서-c와-swift-상호호출하기">Objective-C Runtime을 사용해서 C++와 Swift 상호호출하기</h1>
<p>발표자: 박동하
영상 링크: <a href="https://youtu.be/tb7ITFPhR1Q">C++ Korea Youtube</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++ Korea] 2022 Meet-Up(12월)]]></title>
            <link>https://velog.io/@whale-park18/C-Korea-2022-Meet-Up12%EC%9B%94</link>
            <guid>https://velog.io/@whale-park18/C-Korea-2022-Meet-Up12%EC%9B%94</guid>
            <pubDate>Wed, 16 Aug 2023 13:48:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/whale-park18/post/461c8cd6-a79c-462a-9b58-5eb24a9b502d/image.jpg" alt="[C++ Korea] 2022 Meet-Up(12월)"></p>
<h1 id="windows-app-sdk-winui-3-해봤습니다">Windows App SDK, WinUI 3 해봤습니다</h1>
<p>발표자: 박동하
영상 링크: <a href="https://youtu.be/vJoKFNvLSN8">C++ Korea Youtube</a></p>
<h1 id="vcpkg를-활용한-임베디드-어플리케이션-개발기">vcpkg를 활용한 임베디드 어플리케이션 개발기</h1>
<p>발표자: 정은식
영상 링크: <a href="https://youtu.be/bqUcIJUXRAg">C++ Korea Youtube</a></p>
<h1 id="struct-vs-tuple-성능비교">Struct vs Tuple 성능비교</h1>
<p>발표자: 유양석
영상 링크: <a href="https://youtu.be/YT0ulMEjeyM">C++ Korea Youtube</a></p>
<h1 id="simd-활용기">SIMD 활용기</h1>
<p>발표자: 정승호
영상 링크: <a href="https://youtu.be/oXe1jgR-W2w">C++ Korea Youtube</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022 버닝비버]]></title>
            <link>https://velog.io/@whale-park18/2022-%EB%B2%84%EB%8B%9D%EB%B9%84%EB%B2%84</link>
            <guid>https://velog.io/@whale-park18/2022-%EB%B2%84%EB%8B%9D%EB%B9%84%EB%B2%84</guid>
            <pubDate>Mon, 07 Aug 2023 11:57:33 GMT</pubDate>
            <description><![CDATA[<h1 id="대표-이미지">대표 이미지</h1>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/whale-park18/post/46b27490-c142-4541-bc8c-9b6b2960f840/image.jpg" alt="대표 이미지1"></th>
<th><img src="https://velog.velcdn.com/images/whale-park18/post/0d14d876-000c-4c57-9277-f945b8705a6b/image.jpg" alt="대표 이미지2"></th>
</tr>
</thead>
</table>
<h2 id="길고양이-이야기2">길고양이 이야기2</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/dd1b33c0-ce30-4292-8bee-7c360c000f06/image.jpg" alt="길고양이 이야기2 부스"></p>
<h2 id="더-램지">더 램지</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/b8b111f9-e331-41e4-b69f-443c653e6bdf/image.jpg" alt="더 램지 부스"></p>
<h2 id="리프트-스위퍼">리프트 스위퍼</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/bafa299c-b371-4c97-8f7d-3eb23278959f/image.jpg" alt="리프트 스위퍼 부스"></p>
<h2 id="리플-이펙트">리플 이펙트</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/38656b13-c883-4433-a42a-37a5e4bbe2d1/image.jpg" alt="리플 이펙트 부스"></p>
<h2 id="스키드">스키드</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/b74b5b87-b189-4ddb-ac56-fa66989ae009/image.jpg" alt="스키드 부스"></p>
<h2 id="이라">이라</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/bf670b03-df26-4ccd-893d-f53a024e7d59/image.jpg" alt="이라 부스"></p>
<h2 id="chronosword">ChronoSword</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/b196edce-3d39-4cad-995a-d3409aa021e5/image.jpg" alt="ChronoSword 부스"></p>
<h2 id="project-bs">Project BS</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/61668898-abb3-43a1-8604-8453d94901ec/image.jpg" alt="Project BS 부스"></p>
<ul>
<li>1인 인디 개발</li>
<li>소울라이크 장르</li>
<li>개발 기간 약 1년</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] shared_ptr]]></title>
            <link>https://velog.io/@whale-park18/C-sharedptr</link>
            <guid>https://velog.io/@whale-park18/C-sharedptr</guid>
            <pubDate>Thu, 03 Aug 2023 16:23:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 글은 <code>std::shared_ptr</code>의 소개와 간단한 사용법을 중심으로 설명합니다.</p>
<p><code>std::shared_ptr</code> 클래스가 갖는 멤버 변수나 함수에 대한 자세한 내용은 시간이 되면 나중에 작성하겠습니다.</p>
</blockquote>
<h1 id="설명">설명</h1>
<h2 id="stdshared_ptr란"><code>std::shared_ptr</code>란?</h2>
<p><code>std::shared_ptr</code>는 <code>std::unique_ptr</code>와 달리 <u>자원 객체에 대한 공유 소유권(shared ownership) 의미론을 체현한 스마트 포인터</u>입니다. <code>shared_ptr</code>는 하나의 자원 객체를 여러 포인터 객체가 가리킬 수 있으며 모든 포인터 객체가 자원 객체를 필요하지 않을 때 자원 객체를 해제하도록 설계되어 있습니다.</p>
<p>하지만 어느 시점에 모든 포인터 객체가 자원 객체를 필요하지 않은지 알 수 없습니다. <code>std::shared_ptr</code>는 자원 객체를 가리키는 포인터 객체의 수를 <strong>참조 개수(reference count)</strong>라 명명하고 관리합니다.  참조 개수는 생성자가 호출되면 증가, 소멸자가 호출되면 감소 그리고 복사 배정 연산자가 호출되면 증가와 감소 모두를 수행합니다.</p>
<p>이러한 참조 개수 관리는 성능에 많은 영향을 미치게 됩니다.</p>
<ul>
<li><p><strong><code>std::shared_ptr</code>의 크기는 생 포인터의 두 배이다.</strong><br>자원 객체를 가리키는 생 포인터 뿐만 아니라 참조 개수를 가리키는 생 포인터도 저장해야 하기 때문에 <u>생 포인터의 두 배의 크기</u>를 가지게 됩니다.</p>
</li>
<li><p><strong>참조 개수를 담는 메모리는 반드시 동적으로 할당해야 한다.</strong><br>개념적으로 참조 개수는 포인터 객체가 현재 자원 객체를 가리키는 포인터 객체의 수를 알기 위한 것이기 때문에 자원 객체가 현재 참조 개수를 담을 공간을 따로 마련하지 않습니다. 즉, 참조 개수는 <code>std::shared_ptr</code>가 관리해야 하며 정적 메모리로는 불가능하기 때문에 결국 동적 메모리를 이용해야 합니다. 만약 정적 메모리를 사용하면 참조 개수를 갱신하기 위해 같은 자원 객체를 가리키고 있는 모든 포인터 객체를 알아야합니다 즉, 참조 개수와 같은 자원 객체를 가리키는 모든 포인터 객체의 정보를 담은 공간이 필요하게 되므로 차라리 동적 메모리에 참조 개수를 저장해 같은 자원 객체를 가리키는 포인터 객체들이 접근하는 것이 이상적입니다.</p>
</li>
<li><p><strong>참조 횟수의 증가와 감소가 반드시 원자적 연산이어야 한다.</strong><br>여러 스레드가 참조 개수에 동시 읽고 쓰려고 한다면 <strong>데이터 레이스</strong>가 발생할 수 있습니다. 이를 방지하기 위해 참조 개수는 원자적으로 연산됩니다. 그러다보니 비원자적 연산보다 느릴 수 밖에 없습니다.</p>
</li>
</ul>
<p><code>std::shared_ptr</code>는 <code>std::unique_ptr</code>과 다르게 오직 개별 객체(<code>std::shared_ptr&lt;T&gt;</code>)만을 지원합니다. &#39;<code>std::shared_ptr&lt;T&gt;</code>로 가리키되, 커스텀 삭제자로 배열 해제(<code>delete[]</code>)하면 되지 않나?&#39; 생각할 수 있지만 <code>std::shared_ptr</code>에서 <code>operator[]</code>를 제공하지 않기 때문만 아니라 설계 밖의 일이기 때문에 시스템에 구멍이 생기게 됩니다.</p>
<h3 id="control-block">Control Block</h3>
<p><code>std::shared_ptr</code>는 생 포인터의 두 배의 크기이고 그 중 하나는 참조 개수를 저장하기 위한 것이라 설명했습니다. 정확히 설명하자면 참조 개수는 제어 블록(Control Block)이라 불리는 더 큰 자료구조에 저장되는 하나의 목록이고 <code>std::shared_ptr</code>는 이 제어 블록을 가리킵니다. 이 제어 목록에는 참조 개수뿐만 아니라 커스텀 삭제자, 약한 개수(weak count) 등이 저장됩니다. 이러한 구현 방법이 제일 이상적이라 생각됩니다. 만약 각 항목마다 별도로 관리하게 된다면 <code>std::shared_ptr</code>의 크기는 계속 커지기만 할 것입니다. 하지만 제어 블록에서 모두 관리하기 때문에 <code>std::unique_ptr</code>처럼 커스텀 삭제자를 지정해도 <code>std::shared_ptr</code>의 크기가 변하지 않습니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/e9455f0f-cd82-4128-a3e2-f28ba39c2a60/image.jpg" alt="std::shared_ptr 도식화"></p>
<h2 id="사용법">사용법</h2>
<blockquote>
<p><code>std::shared_ptr</code>를 사용하기 위해선 <code>&lt;memory&gt;</code> 헤더를 포함해야 합니다.</p>
</blockquote>
<h3 id="생성자를-이용한-생성-및-초기화">생성자를 이용한 생성 및 초기화</h3>
<ul>
<li>자원 객체 초기화</li>
<li>소멸자 초기화</li>
</ul>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    void doSomething()
    {
        // ...
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtr1(new Person(&quot;whale&quot;, 10));
    std::shared_ptr&lt;Person&gt; sPtr2(sPtr1);
    std::shared_ptr&lt;Person&gt; sPtr3 = sPtr2;

    (*sPtr1).doSomething();
    sPtr2-&gt;doSomething();

    std::cout &lt;&lt; &quot;sPtr1 참조 개수: &quot; &lt;&lt; sPtr1.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;sPtr2 참조 개수: &quot; &lt;&lt; sPtr2.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;sPtr3 참조 개수: &quot; &lt;&lt; sPtr3.use_count() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
sPtr1 참조 개수: 3
sPtr2 참조 개수: 3
sPtr3 참조 개수: 3
소멸자 호출</code></pre><p><code>std::shared_ptr</code>도 <code>std::unique_ptr</code>처럼 생 포인터와 동일하게 사용할 수 있게 설계되어 있어 생 포인터처럼 사용하면 됩니다.</p>
<p><code>std::shared_ptr</code>도 기본적으로 <code>delete</code>를 통해서 자원 객체를 해제하지만, 커스텀 삭제자를 설정할 수 있습니다. 하지만 커스텀 삭제자를 지원하는 구체적인 방식은 <code>std::unique_ptr</code>과 다릅니다. <code>std::unique_ptr</code>는 커스텀 삭제자를 템플릿의 두 번째 형으로 설정하고 생성자의 두 번째 인자로 전달해야 했지만 <code>std::shared_ptr</code>에서는 생성자의 두 번째 인자로 전달하기만 하면 됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

void FuncDeleter(Person* pPerson)
{
    std::cout &lt;&lt; &quot;함수 커스텀 소멸자 호출&quot; &lt;&lt; std::endl;

    delete pPerson;
}

int main()
{
    auto lambdaDeleter = [](Person* pPerson) {
        std::cout &lt;&lt; &quot;람다 커스텀 삭제자 호출&quot; &lt;&lt; std::endl;

        delete pPerson;
        };

    std::shared_ptr&lt;Person&gt; sPtr1(new Person(&quot;whale&quot;, 10), lambdaDeleter);
    std::shared_ptr&lt;Person&gt; sPtr2(new Person(&quot;park18&quot;, 10), FuncDeleter);
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
생성자 호출
함수 커스텀 소멸자 호출
소멸자 호출
람다 커스텀 삭제자 호출
소멸자 호출</code></pre><h3 id="stdmake_shared를-사용한-생성-및-초기화"><code>std::make_shared</code>를 사용한 생성 및 초기화</h3>
<p><code>std::shared_ptr</code>도 <code>std::unique_ptr</code>과 동일한 이유로 생성자(정확히는 생 포인터를 넘겨 받는 생성자)를 이용한 초기화 방법을 권장하지 않습니다. <code>std::shared_ptr</code>는 생 포인터를 인자로 넘겨 받는다면 첫 번째 생성으로 판단하고 제어 블록을 생성합니다. 즉, 서로 다른 포인터 객체의 생성자가 같은 자원 객체를 가리키는 생 포인터를 넘겨받는다면 서로 다른 제어 블록을 갖게 되는 문제가 발생하게 됩니다.</p>
<p>두 포인터 객체가 같은 자원 객체를 가리키지만 서로 다른 제어 블록을 갖게 된다면 두 포인터 객체 중 한 포인터 객체의 참조 개수가 0이 되어 남은 포인터 객체가 아직 자원 객체를 가리키고 있음에도 불구하고 자원 객체를 해제시켜 버립니다.</p>
<p>만약 운 좋게 남은 포인터 객체에서 자원 객체를 참조하지 않았어도 결국 포인터 객체의 참조 개수가 0이 되어 이미 해제된 자원 객체를 다시 해제시켜 오류를 발생시키게 됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

int main()
{
    auto ptrPerson = new Person(&quot;whale&quot;, 10);

    std::shared_ptr&lt;Person&gt; sPtr1(ptrPerson);
    std::shared_ptr&lt;Person&gt; sPtr2(ptrPerson);

    std::cout &lt;&lt; &quot;sPtr1 참조 개수: &quot; &lt;&lt; sPtr1.use_count() &lt;&lt; std::endl; 
    std::cout &lt;&lt; &quot;sPtr2 참조 개수: &quot; &lt;&lt; sPtr2.use_count() &lt;&lt; std::endl; 
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
sPtr1 참조 개수: 1
sPtr2 참조 개수: 1
소멸자 호출
소멸자 호출

&lt;런타임 오류 발생&gt;</code></pre><p>이 같은 상황을 피하기 위해 포인터를 인자로 넘겨받는 생성자 초기화 방식을 지양하고 <code>std::make_shared</code> 함수의 사용을 지향해 안전하게 포인터 객체를 생성해야 합니다. 한 가지 주의점이 있는데 <code>std::make_shared</code> 함수는 항상 제어 블록을 생성하기 때문에 처음 포인터 객체를 생성하는 경우에만 사용해야 합니다.</p>
<blockquote>
<p><code>std::make_shared</code> 함수는 이 같은 상황을 예견?했던 것인지 모르겠지만 C++11부터 지원했습니다.</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    void DoSomething()
    {
        // ...
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtr = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    sPtr-&gt;DoSomething();
}</code></pre>
<p>하지만 <code>std::make_shared</code> 함수 또한 <code>std::make_unique</code> 함수처럼 커스텀 삭제자를 지정할 수 없다는 단점이 존재합니다. </p>
<h2 id="주의점">주의점</h2>
<h3 id="자기-자신을-가리키는-경우">자기 자신을 가리키는 경우</h3>
<p>프로그램을 개발하다보면 객체 내부에서 자기 자신을 가리키는 포인터 객체를 만들어야 하는 상황이 발생하기도 앖니다. 자기 자신을 가리키기 위해 <code>std::shared_ptr</code>의 생성자에 <code>this</code>를 전달하게 된다면 다음과 같은 상황이 발생합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::shared_ptr&lt;Person&gt; GetSelf() 
    {
        return std::shared_ptr&lt;Person&gt;(this);
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtr1 = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    std::shared_ptr&lt;Person&gt; sPtr2 = sPtr1-&gt;GetSelf();

    std::cout &lt;&lt; &quot;sPtr1 참조 개수: &quot; &lt;&lt; sPtr1.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;sPtr2 참조 개수: &quot; &lt;&lt; sPtr2.use_count() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
sPtr1 참조 개수: 1
sPtr2 참조 개수: 1
소멸자 호출

&lt;런타임 오류 발생&gt;</code></pre><p><code>this</code> 키워드 역시 생 포인터를 전달하는 것이기 때문에 두 포인터 객체는 서로 다른 제어 블록을 가지게 되면서 미정의 행위를 발생시키게 됩니다(예제에서는 간단하게 설명하기 위해 자신을 가리키는 포인터 객체를 생성해 반환했지만 벡터에 자신을 가리키는 포인터를 넣는 등의 상황도 있습니다).</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/f46bac1c-6604-4b48-84d7-ab9496b0bd50/image.jpg" alt="같은 자원 객체를 가리키지만 다른 제어 블록을 갖는 모습"></p>
<p>이 문제는 <code>std::enable_shared_from_this</code>라는 기반 클래스 템플릿을 상속 받고 자신을 가리키는 포인터 객체를 생성해 반환하는 대신 <code>shared_from_this</code> 멤버 함수를 반환하는 것으로 간단하게 해결할 수있습니다.</p>
<p>한 가지 중요한 점은 <code>shared_from_this</code> 멤버 함수가 제대로 작동하기 위해선 해당 객체를 가리키고 있는 포인터 객체를 반드시 먼저 정의해야 한다는 것입니다. 왜냐하면 <code>shared_from_this</code> 멤버 함수는 해당 객체를 가리키고 있는 제어 블록을 확인할 뿐, 제어 블록을 생성하지 않기 때문입니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person : public std::enable_shared_from_this&lt;Person&gt;
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::shared_ptr&lt;Person&gt; GetSelf() 
    {
        return shared_from_this();
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtr1 = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    std::shared_ptr&lt;Person&gt; sPtr2 = sPtr1-&gt;GetSelf();

    std::cout &lt;&lt; &quot;sPtr1 참조 개수: &quot; &lt;&lt; sPtr1.use_count() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;sPtr2 참조 개수: &quot; &lt;&lt; sPtr2.use_count() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
sPtr1 참조 개수: 2
sPtr2 참조 개수: 2
소멸자 호출</code></pre><h3 id="순환-참조">순환 참조</h3>
<p><code>std::shared_ptr</code>은 참조 개수가 0이 되어야 자원을 해제할 수 있습니다. 하지만 객체를 더 이상 사용하지 않음에도 불구하고 참조 개수가 0으로 떨어지지 않는 상황이 존재합니다.</p>
<p>포인터 객체 <code>sPtr1</code>과 <code>sPtr2</code>가 있을 때, 자원 객체의 내부에서 서로를 가리키는 포인터 객체가 있다면 서로를 가리키는 포인터 객체 때문에 참조 개수가 0으로 떨어지지않아 포인터 객체가 소멸되지 않는 상황으로 이런 구조를 <strong>순환 구조</strong>라고 합니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/71e35435-7d4c-4468-9a88-d13851f215bb/image.jpg" alt="순환 참조"></p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person : public std::enable_shared_from_this&lt;Person&gt;
{
private:
    std::string mName;
    int mAge;

    std::shared_ptr&lt;Person&gt; mSPtrFriend;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::shared_ptr&lt;Person&gt; GetSelf() 
    {
        return shared_from_this();
    }

    void SetFriend(std::shared_ptr&lt;Person&gt;&amp; sPtrFriend)
    {
        mSPtrFriend = sPtrFriend;
    }
};

int main()
{
    std::shared_ptr&lt;Person&gt; sPtrPerson1 = std::make_shared&lt;Person&gt;(&quot;whale&quot;, 10);
    std::shared_ptr&lt;Person&gt; sPtrPerson2 = std::make_shared&lt;Person&gt;(&quot;park18&quot;, 20);

    sPtrPerson1-&gt;SetFriend(sPtrPerson2);
    sPtrPerson2-&gt;SetFriend(sPtrPerson1);
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
생성자 호출</code></pre><p>출력 결과를 보면 서로 파괴할 수 없는 상태가 되어 소멸자가 호출되지 않는 것을 확인할 수 있습니다.</p>
<p>이 문제는 <code>std::shared_ptr</code>의 설계 한계에 의한 문제이기 때문에 <code>std::shared_ptr</code>을 통해서 이 문제를 해결할 수는 없습니다. 대신 이 문제를 해결하기 위해 설계된 것이 <code>std::weak_ptr</code>입니다.</p>
<p>다음에는 <code>std::weak_ptr</code>에 대해 알아보겠습니다.</p>
<h1 id="참조">참조</h1>
<p><a href="https://en.cppreference.com/w/cpp/memory/shared_ptr">cppreference - en</a></p>
<p><a href="https://ko.cppreference.com/w/cpp/memory/shared_ptr">cppreference - ko</a></p>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-170">VisualC++ Docs</a></p>
<p><a href="https://www.tcpschool.com/cpp/cpp_template_smartPointer">TCP School</a></p>
<p><a href="https://modoocode.com/252">모두의 코드</a></p>
<p><a href="https://ansohxxn.github.io/cpp/chapter15-6/">식빵맘(ansohxxn)</a></p>
<p><a href="https://ozt88.tistory.com/28">ozt88</a></p>
<p><a href="https://ebook.insightbook.co.kr/book/117">Effective Modern C++</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그 동안 했던 프로젝트 모음]]></title>
            <link>https://velog.io/@whale-park18/%EA%B7%B8-%EB%8F%99%EC%95%88-%ED%96%88%EB%8D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@whale-park18/%EA%B7%B8-%EB%8F%99%EC%95%88-%ED%96%88%EB%8D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Wed, 26 Jul 2023 08:40:16 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://github.com/Park18/TIME_CHART">TIME_CHART</a><br>  → C++ 강의 팀프로젝트</li>
<li><a href="https://github.com/Park18/SHA-256">SHA-256</a><br>  → 개인 프로젝트</li>
<li><a href="https://github.com/Park18/BackupUniversityFile">BackupUniversityFile</a><br>  → 개인 프로젝트</li>
<li><a href="https://github.com/Park18/RansomwareWatcher">RansomwareWAtcher</a><br>  → 캡스톤</li>
<li><a href="https://github.com/Whale-Park18/FPS-Survival">FPS-Survival</a><br>  → 개인 프로젝트</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] unique_ptr]]></title>
            <link>https://velog.io/@whale-park18/C-uniqueptr</link>
            <guid>https://velog.io/@whale-park18/C-uniqueptr</guid>
            <pubDate>Tue, 25 Jul 2023 19:05:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 글은 <code>std::unique_ptr</code>의 소개와 간단한 사용법을 중심으로 설명합니다.</p>
<p><code>std::unique_ptr</code> 클래스가 갖는 멤버 변수나 함수에 대한 자세한 내용은 시간이 되면 나중에 작성하겠습니다.</p>
</blockquote>
<h1 id="설명">설명</h1>
<h2 id="stdunique_ptr란"><code>std::unique_ptr</code>란?</h2>
<p><code>std::unique_ptr</code>는 <u>가리키는 자원 객체에 대한 유일한 소유권을 갖는 독점 소유권(exclusive ownership) 의미론을 체현한 스마트 포인터</u>입니다. <code>std::unique_ptr</code>은 생 포인터(raw pointer)와 같은 크기로 볼 수 있으며, 대부분의 연산(역참조를 비롯해서)에서 <code>std::unique_ptr</code>는 생 포인터와 동일한 명령을 실행할 수 있습니다. 즉, 메모리와 CPU 주기가 넉넉하지 않은 상황에서도 생 포인터가 충분히 작고 충분히 빠른 상황이라면 <code>std::unique_ptr</code>를 사용해도 비슷한 성능을 낼 수 있습니다.</p>
<p><code>std::unique_ptr</code>는 <u>복사를 허용하지 않습니다(복사를 허용할 경우 같은 자원을 가리킬 수 있게 되면서 미정의 행동이 발생합니다). 하지만 소유권 이전은 허용합니다.</u> 즉, 자원 객체의 소유권을 집 명의처럼 복사할 수는 없지만 이전시킬 수 있으며 소유권을 이전 시키려면 <code>std::move</code> 함수를 사용해야 합니다. 소유권이 이전되면 원본 포인터 객체는 널을 가리키게되며 대상 포인터 객체가 자원 객체를 가리키게 됩니다.</p>
<p><code>std::unique_ptr</code>는 자원 객체를 소유하고 있을 때만, 자원 객체를 해제할 수 있는 권한을 가집니다. 또한 포인터 객체는 생성할 때 커스텀 삭제자(custom deleter)를 사용하도록 지정할 수도 있습니다. 다만, 커스텀 삭제자를 지정하게 되면 커스텀 삭제자를 위한 함수 포인터가 필요하므로 <code>std::unique_ptr</code>의 크기가 1 워드에서 2 워드로 증가하게 됩니다.</p>
<p><code>std::unique_ptr</code>는 개별 객체(<code>std::unique_ptr&lt;T&gt;</code>)와 배열(<code>std::unique_ptr&lt;T[]&gt;</code>) 형태 모두 지원합니다. <code>std::unique_ptr</code> API는 사용 대상에게 잘 맞는 형태로 설계되어 있어 개별 객체 형태는 색인 적용 연산(<code>operator[]</code>)를 제공하지 않으며, 배열 형태는 역 참조 연산자들(<code>operator*</code>와 <code>operator-&gt;</code>)제공하지 않습니다.</p>
<p><code>std::unique_ptr</code>에서 배열 형태를 지원하지만 사용을 권장하지 않습니다. 배열 형태의 포인터 객체를 생성하기 보단 <code>std::array</code>나 <code>std::vector</code>, <code>std::string</code>을 사용하는 것이 거의 항상 더 나은 선택이기 때문입니다.</p>
<h2 id="왜-엄격한-소유권을-지니는가">왜 엄격한 소유권을 지니는가?</h2>
<p><code>std::unique_ptr</code>가 이렇게 설계된 이유는 <strong>double-free</strong> 오류에 있습니다.</p>
<p>double-free 오류란 이미 해제한 자원 객체를 다시 해제하는 경우 발생하는 오류로 다음과 같은 상황에서 자주 발생합니다.</p>
<pre><code class="language-cpp">int* ptr1 = new int();
int* ptr2 = ptr1;

// ...

// ptr1 사용 끝, 해제
delete ptr1;

// ..

// ptr2 사용 끝, 해제
delete ptr2;</code></pre>
<p>동일한 자원 객체를 가리키는 포인터 <code>ptr1</code>, <code>ptr2</code>가 존재합니다. <code>ptr1</code>이 자원 객체를 다 사용해, 해제합니다. <code>ptr2</code>는 이 사실을 모르고 있다가 이미 소멸된 자원 객체를 다시 해제하는 불상하가 일어납니다. 이런 경우 메모리 오류가 발생해, 프로그램이 죽게됩니다.</p>
<p>위의 문제가 발생한 이유는 <u>동적 할당된 자원 객체의 소유권이 명확하지 않고 쉽게 접근할 수 있기 때문</u>입니다. 만약 처음 초기화된 포인터에게만 소유권을 갖고 있었다면 위와 같이 자원 객체를 두 번 해제하는 일은 발생하지 않았을 것입니다.</p>
<p>즉, <code>std::unique_ptr</code>는 소유권이 명확하지 않아 발생하는 오류에 의해 발생하는 미정의 행동에서 벗어나기 위해 독점 소유권 의미론을 체현한 스마트 포인터로 설계된 것입니다.</p>
<h2 id="사용법">사용법</h2>
<blockquote>
<p><code>std::unique_ptr</code>를 사용하기 위해선 <code>&lt;memory&gt;</code> 헤더를 포함해야 합니다.</p>
</blockquote>
<h3 id="생성자를-이용한-생성-및-초기화">생성자를 이용한 생성 및 초기화</h3>
<p>스마트 포인터는 모든 타입에 대한 자원 객체를 가리킬 수 있어야 하기 때문에 템플릿 클래스로 선언되어 있습니다. 즉, 스마트 포인터는 생성자를 통해 생성 및 초기화를 할 수 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    void doSomething()
    {
        // ...
    }
};

int main()
{
    std::unique_ptr&lt;Person&gt; uPtr(new Person(&quot;whale&quot;, 10));
    (*uPtr).doSomething();
    uPtr-&gt;doSomething();
}</code></pre>
<p>위에서 언급했듯이 <code>std::unique_ptr</code>는 생 포인터와 동일하게 사용할 수 있게 설계되어 있어 생 포인터처럼 사용하면 됩니다.</p>
<p>또한 <code>std::unique_ptr</code>(와 <code>shared_ptr</code>)는 기본적으로 자원 객체의 해제는 <code>delete</code>를 통해서 일어나지만, 위에서 언급했듯이 커스텀 삭제자를 설정할 수 있습니다. 자원 객체의 해제 전, 로그를 기록하는 등의 작업이 필요할 때 사용합니다.</p>
<p>커스텀 삭제자를 설정하기 위해서는 포인터 객체를 선언할 때, <code>std::unique_ptr</code>의 두 번째 템플릿 형을 설정하고 생성자의 두 번째 인자로 함수 객체를 전달해야 합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::string GetName() { return mName; }
    int GetAge() { return mAge; }
};

void FuncDeleter(Person* pPerson)
{
    std::cout &lt;&lt; &quot;함수 커스텀 소멸자 호출&quot; &lt;&lt; std::endl;

    delete pPerson;
}

int main()
{
    auto lambdaDeleter = [](Person* pPerson) {
        std::cout &lt;&lt; &quot;람다 커스텀 삭제자 호출&quot; &lt;&lt; std::endl;

        delete pPerson;
    };

    std::unique_ptr&lt;Person, decltype(lambdaDeleter)&gt; uPtr1(new Person(&quot;whale&quot;, 10), lambdaDeleter);
    std::unique_ptr&lt;Person, decltype(&amp;FuncDeleter)&gt; uPtr2(new Person(&quot;park18&quot;, 20), FuncDeleter);
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
생성자 호출
함수 커스텀 소멸자 호출
소멸자 호출
람다 커스텀 삭제자 호출
소멸자 호출</code></pre><p><code>lambdaDeleter</code>와 <code>FuncDeleter</code>가 <code>Person</code>에 대한 커스텀 삭제자입니다. <u>모든 커스텀 삭제자 함수는 파괴할 자원 객체를 가리키는 포인터 하나를 받으며, 그 객체를 파괴하는데 필요한 일들을 수행</u>합니다. 지금 예제에서 삭제자는 별도의 일을 하고 있지는 않지만 로그를 남기거나 세이브 파일을 저장하는 등 삭제 전에 필요한 일을 수행하는 코드를 작성할 수 있습니다. 모든 일을 마친 후에는 인자로 넘겨받은 자원 객체를 반드시 해제해야 합니다.</p>
<p>삭제자 함수는 통상적인 함수를 작성해 전달하는 방법도 있지만 람다 표현식을 이용해 작성하는 것이 더 효율적입니다.</p>
<p>커스텀 삭제자를 사용하기 위해 <code>std::unique_ptr</code>의 두 번째 템플릿 형을 지정해야 하며 이를 위해 <code>decltype</code> 키워드를 사용합니다. <code>decltype</code> 키워드는 <code>auto</code> 키워드와 비슷한 개념으로 주어진 표현식의 타입을 컴파일러가 추론해 결정하도록 합니다.</p>
<p>최상위 기본 클래스로 하위 파생 클래스를 관리할 목적으로 포인터 객체를 생성하는 것이라면 삭제자가 인자로 전달받는 것이 기본 클래스 타입이기 때문에 기본 클래스의 소멸자를 가상화 시켜야합니다. 만약 기반 클래스의 소멸자를 가상화 시키지 않았다면 기본 클래스의 소멸자만 호출되어 미정의 행위가 발생하게됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class BaseClass
{
public:
    BaseClass()
    {
        std::cout &lt;&lt; &quot;(기본 클래스) 생성자 호출&quot; &lt;&lt; std::endl;
    }

    virtual ~BaseClass()
    {
        std::cout &lt;&lt; &quot;(기본 클래스) 소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

class DerivedClass1 : public BaseClass
{
public:
    DerivedClass1()
    {
        std::cout &lt;&lt; &quot;(파생 클래스1) 생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~DerivedClass1()
    {
        std::cout &lt;&lt; &quot;(파생 클래스1) 소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

class DerivedClass2 : public BaseClass
{
public:
    DerivedClass2()
    {
        std::cout &lt;&lt; &quot;(파생 클래스2) 생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~DerivedClass2()
    {
        std::cout &lt;&lt; &quot;(파생 클래스2) 소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

int main()
{
    auto deleter = [](BaseClass* pBaseClass) {
        std::cout &lt;&lt; &quot;커스텀 삭제자 호출&quot; &lt;&lt; std::endl;

        delete pBaseClass;
    };

    std::unique_ptr&lt;DerivedClass1, decltype(deleter)&gt; uPtr1(new DerivedClass1, deleter);
    std::unique_ptr&lt;DerivedClass2, decltype(deleter)&gt; uPtr2(new DerivedClass2, deleter);
}</code></pre>
<pre><code>[출력 결과]

(기본 클래스) 생성자 호출
(파생 클래스1) 생성자 호출
(기본 클래스) 생성자 호출
(파생 클래스2) 생성자 호출
커스텀 삭제자 호출
(파생 클래스2) 소멸자 호출
(기본 클래스) 소멸자 호출
커스텀 삭제자 호출
(파생 클래스1) 소멸자 호출
(기본 클래스) 소멸자 호출</code></pre><h3 id="stdmake_unique를-사용한-생성-및-초기화"><code>std::make_unique</code>를 사용한 생성 및 초기화</h3>
<p><code>std::unique_ptr</code>를 생성자로 초기화하기 위해선 반드시 주소값이 필요합니다. 만약 인자로 다른 포인터 객체가 가리키고 있는 자원 객체의 주소를 전달하여 초기화하는 것이 가능한지 의문이 생깁니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

int main()
{
    int* pNumber = new int(5);
    std::unique_ptr&lt;int&gt; uPtr1(pNumber);
    std::unique_ptr&lt;int&gt; uPtr2(pNumber);
    std::unique_ptr&lt;int&gt; uPtr3;
    uPtr3.reset(pNumber);

    std::cout &lt;&lt; &quot;uPtr1: &quot; &lt;&lt; uPtr1.get() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;uPtr2: &quot; &lt;&lt; uPtr2.get() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;uPtr3: &quot; &lt;&lt; uPtr3.get() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]
uPtr1: 01450428
uPtr2: 01450428
uPtr3: 01450428

&lt;런타임 오류&gt;</code></pre><p>놀랍게도 서로 다른 포인터 객체가 같은 자원 객체를 가리킬 수 있게 초기화하는 것이 가능합니다. 이렇게 되면 <code>std::unique_ptr</code>는 목적과 다르게 하나의 자원 객체를 여러 개의 포인터 객체가 소유하게 되며 double-free 문제까지 발생하게 됩니다.</p>
<blockquote>
<p><code>std::unique_ptr</code>가 다른 자원 객체를 가리키고 싶게 하고 싶다면 <code>reset</code> 멤버 함수를 호출하면 됩니다.</p>
<p><code>std::unique_ptr</code>가 현재 가리키고 있는 자원 객체의 주소를 알고 싶으면 <code>get</code> 멤버 함수를 호출하면 됩니다.</p>
</blockquote>
<p>이런 상황을 피하기 위해 C++14에서는 <code>std::make_unique</code> 함수를 제공합니다. <code>std::make_unique</code> 함수는 자원 객체의 생성자에게 전달할 인자들을 전달 받고 내부에서 <code>std::unique_ptr</code>를 생성해 반환하기 때문에 안전하게 포인터 객체를 생성할 수 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    void DoSomething()
    {
        // ...
    }
};

int main()
{
    std::unique_ptr&lt;Person&gt; uPtr = std::make_unique&lt;Person&gt;(&quot;whale&quot;, 10);
    uPtr-&gt;DoSomething();
}</code></pre>
<p>이렇게 안전하게 포인터 객체를 생성해주는 <code>std::make_unique</code> 함수에도한 가지 단점이 존재합니다. 바로 커스텀 삭제자를 설정할 수 없다는 것입니다. 때문에 커스텀 삭제자를 지정해야 하는 경우에는 어쩔 수 없이 생성자를 통해 초기화해야 합니다.</p>
<blockquote>
<p>같은 스마트 포인터인 <code>std::shared_ptr</code>의 경우에는 C++14가 아닌 C++11에 <code>std::make_shared</code> 함수가 존재합니다. 아마 C++11에는 이런 상황을 예상하지 못해 제외시킨 것 같습니다.</p>
</blockquote>
<blockquote>
<p>C++11 환경에서 개발 중이라면 <code>std::make_unique</code>의 기본 버전을 직접 작성하는 것이 어렵지 않기 때문에 걱정하지 않아도 됩니다.</p>
<pre><code class="language-cpp">template&lt;typename T, typename... Ts&gt;
std::unique_ptr&lt;T&gt; make_unique&lt;Ts&amp;... params&gt;
{
    return std::unique_ptr&lt;T&gt;(new T(std::forward&lt;Ts&gt;(params)...));
}</code></pre>
<p>단, 직접 구현할 경우 <code>std::</code> 네임스페이스를 적성하지 않아야 합니다. 나중에 C++14로 업그레이드 했을 경우 표준 라이브러리와 이름 충돌이 발생하기 때문입니다.</p>
</blockquote>
<h3 id="복사">복사</h3>
<p><code>std::unqie_ptr</code>은 복사할 수 없다고 설명했습니다. 그럼에도 불구하고 복사를 시도할 경우 어떻게 작동할까요?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }
};

int main()
{
    std::unique_ptr&lt;Person&gt; uPtrOriginal = std::make_unique&lt;Person&gt;(&quot;whale&quot;, 10);
    std::unique_ptr&lt;Person&gt; uPtrCopy(uPtrOriginal);
}</code></pre>
<pre><code>&lt;컴파일 오류&gt;

심각도    코드    설명    프로젝트    파일    줄    비표시 오류(Suppression) 상태
오류    C2280    &#39;std::unique_ptr&lt;Person,std::default_delete&lt;Person&gt;&gt;::unique_ptr(const std::unique_ptr&lt;Person,std::default_delete&lt;Person&gt;&gt; &amp;)&#39;: 삭제된 함수를 참조하려고 합니다.</code></pre><p>복사를 시도할 경우 위와 같은 오류가 발생하게 됩니다. 위 오류는 삭제된 함수를 사용하려 했을 때, 발생하는 것입니다.</p>
<p>삭제된 함수란 C++11에서 추가된 기능으로 프로그래머가 명시적으로 &#39;이 함수는 사용하지 말 것!&#39;을 표현한 것입니다. 혹시라도 삭제된 함수를 사용할 경우 위와 같이 컴파일 오류가 발생하게 됩니다.</p>
<p><code>std::unique_ptr</code>의 경우 <strong>유일하게 소유하는 엄격한 소유권</strong> 때문에 복사 생성자와 대입 연산자(복사 기능 한정)를 <em>명시적으로 삭제</em>했습니다. 만약 복사 생성자나 대입 연산자를 사용할 수 있다면 유일한 소유권을 갖는 특성이 사라지게 되면서 <code>std::unique_ptr</code>는 존재 의의를 잃게 됩니다.</p>
<h3 id="stdmove-함수를-사용한-소유권-이전"><code>std::move</code> 함수를 사용한 소유권 이전</h3>
<p><code>std::unique_ptr</code>는 복사는 불가능 하지만 소유권을 이전할 수 있습니다. 다만, 소유권을 이전하기 위해선, <code>std::move</code> 함수를 사용해야 합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

int main()
{
    std::unique_ptr&lt;int&gt; uPtrOriginal = std::make_unique&lt;int&gt;(1);
    std::unique_ptr&lt;int&gt; uPtrTarget(nullptr);

    std::cout &lt;&lt; &quot;[Before] owner: uPtrOriginal&quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;uPtrOriginal: &quot; &lt;&lt; uPtrOriginal.get() &lt;&lt; &quot;, uPtrTarget: &quot; &lt;&lt; uPtrTarget.get() &lt;&lt; std::endl;
    std::cout &lt;&lt; &#39;\n&#39;;

    uPtrTarget = std::move(uPtrOriginal);

    std::cout &lt;&lt; &quot;[After] owner: uPtrTarget&quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;uPtrOriginal: &quot; &lt;&lt; uPtrOriginal.get() &lt;&lt; &quot;, uPtrTarget: &quot; &lt;&lt; uPtrTarget.get() &lt;&lt; std::endl;
}</code></pre>
<pre><code>[출력 결과]

[Before] owner: uPtrOriginal
uPtrOriginal: 00BF03F0, uPtrTarget: 00000000

[After] owner: uPtrTarget
uPtrOriginal: 00000000, uPtrTarget: 00BF03F0</code></pre><p><code>std::move</code> 함수를 사용해, <code>uPtrOriginal</code>의 소유권을 <code>uPtrTarget</code>에게 이전하였습니다. <code>uPtrOriginal</code>이 가리키던 주소는 널을 가리키게 되고 <code>uPtrTarget</code>은 <code>uPtrOriginal</code>이 가리키던 자원 객체를 가리키면서 소유권이 넘겨진 것을 확인할 수 있습니다.</p>
<blockquote>
<p>소유권이 이전된 포인터 객체를 댕글리 포인터(dangling pointer)라고 하며 이를 재참조할 때, 런타임 오류가 발생합니다. 따라서 소유권 이전은 댕글링 포인터를 다시 참조하지 않겠다는 확신을 갖고 이동시켜야 합니다.</p>
</blockquote>
<h3 id="함수-인자로-전달하기">함수 인자로 전달하기</h3>
<p>일반적으로 함수 인자를 전달하게 되면 복사가 발생하며 전달하게 됩니다. 하지만  <code>std::unique_ptr</code>은 위에서 설명했듯이 복사가 불가능합니다. 즉, <code>std::unique_ptr</code>은 함수 인자로 전달할 수 없다는 것입니다.</p>
<p>하지만 복사가 일어나지 않는 레퍼런스를 전달한다면 가능하지 않을까요?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::string GetName() { return mName; }
    int GetAge() { return mAge; }
};

inline void PrintPersonalInfo(std::unique_ptr&lt;Person&gt;&amp; uPtrReference)
{
    std::cout &lt;&lt; &quot;이름: &quot; &lt;&lt; uPtrReference-&gt;GetName() &lt;&lt; std::endl;

    std::cout &lt;&lt; &quot;나이: &quot; &lt;&lt; uPtrReference-&gt;GetAge() &lt;&lt; std::endl;
}

int main()
{
    std::unique_ptr&lt;Person&gt; uPtrOriginal = std::make_unique&lt;Person&gt;(&quot;whale&quot;, 10);
    PrintPersonalInfo(uPtrOriginal);
}</code></pre>
<pre><code>[출력 결과]

생성자 호출
이름: whale
나이: 10
소멸자 호출</code></pre><p>포인터 객체를 레퍼런스로 전달할 경우 정상적으로 작동하는 것을 확인할 수 있습니다. <code>PrintPersonalInfo</code> 함수의 <code>uPtrPerson</code> 인자는 레퍼런스이기 때문에 함수가 종료되도 객체를 파괴되지 않고 <code>main</code> 함수가 종료되면서 파괴는 것을 확인할 수도 있습니다.</p>
<blockquote>
<p>참고한 자료 중에서 레퍼런스이긴 하지만 유일하게 소유한다는 원칙을 벗어나기 때문에 문맥상 옳지 못하다고 의견을 내신 분도 있습니다.</p>
</blockquote>
<blockquote>
<p>위에선 값으로 전달하지 못한다고 했지만 정확하게 말하자면 불가능 한 것은 아닙니다. 소유권을 <code>get</code> 멤버 함수를 사용해 포인터 주소값을 전달하거나 <code>std::move</code> 함수를 사용해 소유권을 넘기는 것입니다. 하지만 소유권을 넘기게 된다면 함수가 끝날 때, 자원을 반환한다는 문제가 발생합니다.</p>
</blockquote>
<h3 id="컨테이너의-원소">컨테이너의 원소</h3>
<p>C++는 <code>std::array</code>, <code>std::vector</code> 등의 다양한 STL을 지원하면서 C++로 프로그램을 개발하게 된다면 STL을 반드시 접하게 됩니다. 이 STL의 컨테이너의 원소로 <code>std::unique_ptr</code>를 저장할 수 있을까요?</p>
<p>기본적으로 <code>std::vector</code>의 <code>push_back</code> 멤버 함수 같이 컨테이너에 원소를 넣게 된다면 복사 과정이 발생하게 됩니다. 즉, <code>std::unique_ptr</code>는 컨테이너의 원소가 될 수 없다는 것을 의미합니다. 하지만 <code>std::move</code> 함수로 소유권을 이전 시킨다면 컨테이너의 원소로 넣을 수 있습니다.</p>
<p>다른 방법으로 <code>std::vecto</code>의 <code>emplace_back</code> 멤버 함수 같이 컨테이너 내부에서 생성하게 된다면 복사 과정이 발생하지 않기 때문에 컨테이너의 원소가 될 수 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;

class Person
{
private:
    std::string mName;
    int mAge;

public:
    Person(std::string name, int age) : mName(name), mAge(age)
    {
        std::cout &lt;&lt; &quot;생성자 호출&quot; &lt;&lt; std::endl;
    }

    ~Person()
    {
        std::cout &lt;&lt; &quot;소멸자 호출&quot; &lt;&lt; std::endl;
    }

    std::string GetName() { return mName; }
    int GetAge() { return mAge; }
};

void funcDeleter(Person* pPerson)
{
    std::cout &lt;&lt; &quot;함수 커스텀 소멸자 호출&quot; &lt;&lt; std::endl;

    delete pPerson;
}

int main()
{
    std::vector&lt;std::unique_ptr&lt;Person&gt;&gt; vec;

    std::unique_ptr&lt;Person&gt; uPtr = std::make_unique&lt;Person&gt;(&quot;whale&quot;, 10);

    vec.push_back(std::move(uPtr));
    vec.emplace_back(new Person(&quot;park18&quot;, 20));

    for (auto&amp; item : vec)
    {
        std::cout &lt;&lt; &quot;name: &quot; &lt;&lt; item-&gt;GetName() &lt;&lt; &quot;, age: &quot; &lt;&lt; item-&gt;GetAge() &lt;&lt; std::endl;
    }
}</code></pre>
<p>범위 기반 for문은 기본적으로 복사된 원소에 접근하는 것이기 때문에 사용하려면 레퍼런스로 지정해야합니다. 일반 for문은 인덱스를 이용해 직접 접근하는 것이기 때문에 평소처럼 사용하면 됩니다.</p>
<h1 id="참고">참고</h1>
<p><a href="https://en.cppreference.com/w/cpp/memory/unique_ptr">cppreference - en</a></p>
<p><a href="https://ko.cppreference.com/w/cpp/memory/unique_ptr">cppreference - ko</a></p>
<p><a href="https://cplusplus.com/reference/memory/unique_ptr/?kw=unique_ptr">cplusplus.com</a></p>
<p><a href="https://runebook.dev/ko/docs/cpp/memory/unique_ptr">Runebook.dev</a></p>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-170">VisualC++ Docs</a></p>
<p><a href="https://www.tcpschool.com/cpp/cpp_template_smartPointer">TCP School</a></p>
<p><a href="https://modoocode.com/229">모두의 코드</a></p>
<p><a href="https://ansohxxn.github.io/cpp/chapter15-5/#unique_ptr%EC%9D%98-%ED%95%A8%EC%88%98%EB%93%A4">식빵맘(ansohxxn)</a></p>
<p><a href="https://duragon.gitbooks.io/c-11/content/chapter8.html">duragon.gitbooks.io</a></p>
<p><a href="https://gamdekong.tistory.com/88">gamdekong</a></p>
<p><a href="https://www.youtube.com/watch?v=oNqm04uL3v8">코드없는 프로그래밍</a></p>
<p><a href="https://www.youtube.com/watch?v=MGVSPZoOchE">포프TV</a></p>
<p><a href="https://ebook.insightbook.co.kr/book/117">Effective Modern C++</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] Smart Pointer]]></title>
            <link>https://velog.io/@whale-park18/C-Smart-Pointer</link>
            <guid>https://velog.io/@whale-park18/C-Smart-Pointer</guid>
            <pubDate>Tue, 25 Jul 2023 18:54:14 GMT</pubDate>
            <description><![CDATA[<h1 id="설명">설명</h1>
<p>C#이나 Java같은 프로그래밍 언어에는 가비지 컬렉터(<strong>G</strong>arbage <strong>C</strong>ollector:GC)라는 자원 청소기가 기본적으로 내장되어 있습니다. 이 가비지 컬렉터는 프로그램에서 더 이상 사용하지 않는 자원을 자동으로 해제하는 역할을 수행하기 때문에 프로그래머가 자원을 해제하는 것에 대한 신경을 쓰지 않아도 됩니다.</p>
<p>하지만 C++에는 이러한 개념이 존재하지 않아 프로그래머가 수동으로 메모리를 해제해야 합니다. 만약 사용한 자원을 해제하지 않으면 프로그램이 종료되기 전까지, 메모리에 계속 남아있는 <strong>메모리 누수(Memory leak)</strong>가 발생합니다.</p>
<p>&#39;프로그래머가 신경 써서 메모리 해제만 했어도 일어나지 않았을 문제 아닌가?&#39; 생각할 수 있습니다. 객관적으로 보자면 신경쓰지 못한 프로그래머의 잘못이 맞습니다. 하지만 우리는 사람이기 때문에 실수로 메모리 해제를 잊을 수도, 언어의 사양을 잘못 파악하거나 예상치 못한 곳에서 다른 흐름이 발생해 메모리가 해제되지 않는 실수를 할 수 밖에 없습니다.</p>
<p>예를 들어 소멸자를 이용해 메모리 해제하는 코드를 작성했지만 <code>thrower()</code> 함수로 발생한 예외로 인해, 흐름이 바뀌게 되어 메모리를 해제하지 못하고 넘어가 소멸자가 실행되지 못하는 경우가 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

class Socket
{
private:
    int* p_data;

public:
    Socket()
    {
        p_data = new int[100];
        std::cout &lt;&lt; &quot;Get Resource&quot; &lt;&lt; std::endl;
    }

    ~Socket()
    {
        delete[] p_data;
        std::cout &lt;&lt; &quot;Free Resource&quot; &lt;&lt; std::endl;
    }
};

inline void thrower()
{
    // 예외 발생
    throw 1;
}

inline void do_something()
{
    Socket* p_socket = new Socket();

    // ...

    thrower();

    // ...

    delete p_socket;
}

int main()
{
    try
    {
        do_something();
    }
    catch (int i)
    {
        std::cout &lt;&lt; &quot;Exception&quot; &lt;&lt; std::endl;
    }
}</code></pre>
<pre><code>[출력 결과]
Get Resource
Exception</code></pre><p>이러한 문제를 해결하기 위해 C++에서 자원을 관리하는 방법으로 RAII(Resource Acquisition Is Initialization)라는 디자인 패턴을 권장합니다. 이는 자원 관리를 스택에 할당한 객체를 통해 수행하는 것입니다.</p>
<p>스택을 이용하는 이유는 스택에 할당된 객체는 스코프를 벗어나게 되면 자동으로 스택에서 제거되면서 소멸자가 호출되기 때문입니다. 예를 들어 위 예제에서 <code>p_socket</code>은 객체가 아니기 때문에 소멸자가 호출되지 않은 것입니다. 즉, <code>p_socket</code>을 일반적인 포인터가 아닌, 포인터 <strong>객체로 만들어서 자신이 소멸될 때, 자신이 가리키고 있는 자원을 해제하면 되는 것입니다.</strong></p>
<p>이렇게 똑똑하게 작동하는 포인터 객체를 스마트 포인터라고 하며 C++11에서 언어 자체에서 스마트 포인터를 지원하게 됐습니다. C++11 이전에도 <code>auto_ptr</code> 이라는 스마트 포인터가 존재했지만, <a href="https://stackoverflow.com/questions/3697686/why-is-auto-ptr-being-deprecated">문제가 많아</a> C++11로 개정되면서 <code>memory</code> 헤더에 정의된 <code>unique_ptr</code>, <code>shared_ptr</code>, <code>weak_ptr</code>로 개편되어 이제 사용하지 않습니다.</p>
<blockquote>
<p><code>auto_ptr</code> 키워드는 C++11 에서는 deprecated 되었고, C++17에서는 removed 되었습니다.</p>
</blockquote>
<p>그렇다면 스마트 포인터를 사용하면 정말로 문제가 해결되는지 확인해보겠습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

using namespace std;

class Socket
{
private:
    int* p_data;

public:
    Socket()
    {
        p_data = new int[100];
        std::cout &lt;&lt; &quot;Get Resource&quot; &lt;&lt; std::endl;
    }

    ~Socket()
    {
        delete[] p_data;
        std::cout &lt;&lt; &quot;Free Resource&quot; &lt;&lt; std::endl;
    }

    void something()
    {
        cout &lt;&lt; &quot;Socket&#39;s something...&quot; &lt;&lt; endl;
    }
};

inline void thrower()
{
    // 예외 발생
    throw 1;
}

inline void do_something()
{
    unique_ptr&lt;Socket&gt; p_socket(new Socket());
    p_socket-&gt;something();

    thrower();

    // ...
}

int main()
{
    try
    {
        do_something();
    }
    catch (int i)
    {
        std::cout &lt;&lt; &quot;Exception&quot; &lt;&lt; std::endl;
    }
}</code></pre>
<pre><code class="language-plaintext">[출력 결과]
Get Resource
Socket&#39;s something...
Free Resource
Exception</code></pre>
<p><code>thrower</code>에 의해 스코프를 벗어나자 스마트 포인터가 스택에서 해제되면서 소멸자를 호출하는 것을 확인할 수 있습니다.</p>
<h1 id="참고">참고</h1>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170">VisualC++ Docs</a></p>
<p><a href="https://www.tcpschool.com/cpp/cpp_template_smartPointer">TCP School</a></p>
<p><a href="https://modoocode.com/229">모두의 코드</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[STL] array]]></title>
            <link>https://velog.io/@whale-park18/STL-array</link>
            <guid>https://velog.io/@whale-park18/STL-array</guid>
            <pubDate>Tue, 18 Jul 2023 16:25:17 GMT</pubDate>
            <description><![CDATA[<h1 id="구문">구문</h1>
<pre><code class="language-cpp">template &lt;class Ty, size_t N&gt;
class array;</code></pre>
<h1 id="설명">설명</h1>
<p><code>std::array</code> 컨테이너는 C 스타일 배열과 같은 형식을 유지하면서, STL에서 추가된 반복자, 알고리즘, 대입 연산자 등을 사용할 수 있게 있어 많은 편리함을 제공하는 컨테이너입니다. 배열을 사용하기 하려면 <code>array</code> 헤더를 포함해야 합니다.</p>
<p>배열(<code>array</code>)은 STL의 컨테이너 중 하나이기 때문에 템플릿으로 정의되어 있습니다. 또한 고정된 길이의 선형 구조이기 때문에 길이에 대한 정보도 필요합니다. 즉, 배열을 선언하기 위해서는 저장할 타입과 배열의 길이의 정보가 필요합니다.</p>
<pre><code class="language-cpp">#include &lt;array&gt;

int main()
{
    std::array&lt;int, 5&gt; arr;
}</code></pre>
<h2 id="멤버-함수">멤버 함수</h2>
<p>STL에서 제공하는 컨테이너가 제공하는 멤버 함수는 거의 비슷합니다. 때문에 첫 번째인 현재 글 이후에는 간단하게 생략하겠습니다.</p>
<ul>
<li><p><a href="#%EC%98%88%EC%A0%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%B0%8F-%EC%B4%88%EA%B8%B0%ED%99%94">생성자</a></p>
</li>
<li><p><a href="#%EC%98%88%EC%A0%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%B0%8F-%EC%B4%88%EA%B8%B0%ED%99%94"><code>operator=</code></a></p>
</li>
<li><p><a href="#%EC%98%88%EC%A0%9C-%EB%B0%98%EB%B3%B5%EC%9E%90">반복자</a></p>
<ul>
<li><code>begin()</code>
  → 배열(또는 컨테이너)의 첫 번쨰 원소를 가리키는 반복자를 반환한다.</li>
<li><code>end()</code>
  → 배열의 마지막 원소의 <u><strong>다음 원소</strong></u>를 가리키는 반복자를 반환한다.</li>
<li><code>rbegin()</code>
  → 배열의 반대 방향의 첫 번째 원소를 가리키는 반복자를 반환한다.</li>
<li><code>rend()</code>
  → 배열의 반대 방향의 마지막 원소의 <u><strong>다음 원소</strong></u>를 가리키는 반복자를 반환한다.</li>
<li><code>cbegin()</code>
  → 배열의 첫 번째 원소를 가리키는 <code>const</code> 반복자를 반환한다.</li>
<li><code>cend()</code>
  → 배열의 마지막 원소의 <u><strong>다음 원소</strong></u>를 가리키는 <code>const</code> 반복자를 반환한다.</li>
<li><code>crbegin()</code>
  → 배열의 반대 방향의 첫 번째 원소를 가리키는 <code>const</code> 반복자를 반환한다.</li>
<li><code>crend()</code>
  → 배열의 반대 방향의 마지막 원소의 <u><strong>다음 원소</strong></u>를 가리키는 <code>const</code> 반복자를 반환한다.</li>
</ul>
</li>
<li><p><a href="#%EC%98%88%EC%A0%9C-%EC%9B%90%EC%86%8C-%EC%A0%91%EA%B7%BC">원소 접근</a></p>
<ul>
<li><code>at(sizt_type pos)</code>
  → <code>pos</code> 위치에 있는 원소를 반환한다.</li>
<li><code>operator[](size_type pos)</code>
  → <code>pos</code> 위치에 있는 원소를 반환한다.</li>
<li><code>front()</code>
  → 배열의 첫 번째 원소를 반환한다.</li>
<li><code>back()</code>
  → 배열의 마지막 원소를 반환한다.</li>
<li><code>data()</code>
  → 배열의 첫 번째 원소의 주소를 반환합니다.</li>
</ul>
</li>
<li><p><a href="#%EC%98%88%EC%A0%9C-%ED%81%AC%EA%B8%B0">크기</a></p>
<ul>
<li><code>empty()</code>
  → 배열이 비어있는지 확인한다.</li>
<li><code>max_size()</code>
  → 배열의 최대 크기를 반환한다.
  → <code>array</code>에서는 <code>size()</code>와 같다.</li>
<li><code>size()</code>
  → 배열의 크기를 반환한다.
  → <code>array</code>에서는 <code>max_size()</code>와 같다.</li>
</ul>
</li>
<li><p><a href="#%EC%98%88%EC%A0%9C-%EA%B7%B8-%EC%99%B8">그 외</a></p>
<ul>
<li><code>fill(Ty&amp; val)</code>
  → 배열의 모든 원소를 <code>val</code>로 바꾼다.</li>
<li><code>swap(array&amp; right)</code>
  → 두 배열의 내용을 바꾼다.</li>
</ul>
</li>
</ul>
<h2 id="예제">예제</h2>
<h3 id="예제-생성자-및-초기화">예제: 생성자 및 초기화</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 3&gt; arr1 = { 1, 2, 3 };   // 지정된 값으로 초기화
    array&lt;int, 2&gt; arr2;                 // 쓰레기 값으로 초기화
    array&lt;int, 4&gt; arr3 = { 0 };         // 모든 원소 0으로 초기화
    array&lt;int, 5&gt; arr4 = { 10 };        // 첫 번째 원소만 10으로 초기화, 나머지 0으로 초기화
    array&lt;int, 3&gt; arr5{4, 5, 6};        // C++11에서 추가된 초기화 방법
    array&lt;int, 4&gt; arr6 = arr3;          // 대입 연산자를 사용한 초기화

    cout &lt;&lt; &quot;=====arr1=====&quot; &lt;&lt; endl;
    for (auto item : arr1)
        cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====arr2=====&quot; &lt;&lt; endl;
    for (auto item : arr2)
        cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====arr3=====&quot; &lt;&lt; endl;
    for (auto item : arr3)
        cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====arr4=====&quot; &lt;&lt; endl;
    for (auto item : arr4)
        cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====arr5=====&quot; &lt;&lt; endl;
    for (auto item : arr5)
        cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====arr6=====&quot; &lt;&lt; endl;
    for (auto item : arr6)
        cout &lt;&lt; item &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
=====arr1=====
1
2
3
=====arr2=====
-858993460
-858993460
=====arr3=====
0
0
0
0
=====arr4=====
10
0
0
0
0
=====arr5=====
4
5
6
=====arr6=====
0
0
0
0</code></pre><p><code>array</code>를 초기화 하는 방법은 변수 선언과 동시에 초기화하거나 선언 후, 인덱스를 통해 접근해 초기화 하는 등 C 스타일 배열과 동일하게 초기화할 수 있습니다. 초기화를 하지 않았을 경우 쓰레기 값이 들어가는 것 또한 동일합니다.</p>
<p>다른 점이 있다면 <code>operator=</code> 연산자를 사용한 덮어쓰기가 가능하다는 것입니다. 하지만 주의할 점이 존재합니다. 다른 컨테이너의 정보를 덮어쓸 경우에는 오버헤드가 발생하지 않지만 초기화 리스트를 덮어쓸 때는 </p>
<p>다른 컨테이너의 정보를 <code>operator=</code> 연산자를 이용해 복사하는 것은 오버헤드가 발생하지 않지만 초기화 리스트를 복사할 경우, 초기화 리스트의 정보들을 복사한 후(새롭게 변수 선언 == 기본 생성자), 복사(복사 생성자)하는 오버헤드가 발생합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

class Type
{
public:
    Type()
    {
        cout &lt;&lt; &quot;기본 생성자&quot; &lt;&lt; endl;
    }

    Type(Type&amp; other)
    {
        cout &lt;&lt; &quot;복사 생성자&quot; &lt;&lt; endl;
    }
};

int main()
{
    Type type;
    array&lt;Type, 1&gt; arr;
    arr = { type };
}</code></pre>
<pre><code>[출력 결과]
기본 생성자
기본 생성자
복사 생성자</code></pre><h3 id="예제-반복자">예제: 반복자</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 5&gt; arr = { 1, 2, 3, 4, 5 };

    cout &lt;&lt; &quot;=====begin - end=====&quot; &lt;&lt; endl;
    for (array&lt;int, 5&gt;::iterator iter = arr.begin(); iter != arr.end(); iter++)
        cout &lt;&lt; *iter &lt;&lt; endl;

    cout &lt;&lt; &quot;=====rbegin - rend=====&quot; &lt;&lt; endl;
    for (auto iter = arr.rbegin(); iter != arr.rend(); iter++)
        cout &lt;&lt; *iter &lt;&lt; endl;

    cout &lt;&lt; &quot;=====Range-for=====&quot; &lt;&lt; endl;
    for(auto item : arr)
        cout &lt;&lt; item &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
=====begin - end=====
1
2
3
4
5
=====rbegin - rend=====
5
4
3
2
1
=====Range-for=====
1
2
3
4
5</code></pre><p>반복자를 변수로 사용하기 위해서는 <code>컨테이너 템플릿 선언문</code> <code>::</code> <code>iterator</code> 구문으로 선언해야 합니다. <code>컨테이너 템플릿 선언문</code>은 위의 예제로 설명하자면 <code>arr</code>의 변수 타입인 <code>array&lt;int, 5&gt;</code>입니다. 
반복자를 사용할 때마다 이렇게 반복자를 선언하는 것음 참으로 귀찮고 오타로 인한 에러가 발생할 수도 있습니다. 때문에 C++11에서는 <code>auto</code>라는 강력한 추론 타입이 존재하기 때문에 <code>auto</code>를 사용하는 것을 권합니다.</p>
<p><a href="%5BSTL%5D%20Standard%20Template%20Library.md">STL</a>에서 반복자는 포인터의 개념을 품은(또는 비슷한) 객체로 설명했습니다. 때문에 반복자로 데이터에 접근하기 위해서는 포인터 변수처럼 참조 연산자(<code>*</code>)를 사용해 접근해야 합니다.</p>
<p>또한 기본 시퀀스를 반환하는 반복자(<code>begin</code>, <code>end</code> 등)과 반전 시퀀스를 반환하는 반복자(<code>rbegin</code>, <code>rend</code> 등)는 서로 다른 타입이기 때문에 비교 연산자를 이용한 비교가 불가능합니다.</p>
<blockquote>
<p><code>begin</code>, <code>end</code> 등의 기본 시퀀스 반복자 타입: <code>iterator</code>
<code>rebgin</code>, <code>rend</code> 등의 반전 시퀀스 반복자 타입: <code>reverse_iterator</code></p>
</blockquote>
<blockquote>
<p><code>cbegin</code>, <code>cend</code>, <code>crbegin</code>, <code>crend</code>는 <code>begin</code>, <code>end</code>, <code>rbegin</code>, <code>rend</code>에서 <code>const</code>만 적용한 것이기 때문에 생략했습니다.</p>
</blockquote>
<h3 id="예제-원소-접근">예제: 원소 접근</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 5&gt; arr = { 1, 2, 3, 4, 5 };

    cout &lt;&lt; &quot;at(2): &quot; &lt;&lt; arr.at(2) &lt;&lt; endl;
    cout &lt;&lt; &quot;[3]: &quot; &lt;&lt; arr[3] &lt;&lt; endl;
    cout &lt;&lt; &quot;front: &quot; &lt;&lt; arr.front() &lt;&lt; endl;
    cout &lt;&lt; &quot;back: &quot; &lt;&lt; arr.back() &lt;&lt; endl;
    cout &lt;&lt; &quot;data: &quot; &lt;&lt; arr.data() &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
at(2): 3
[3]: 4
front: 1
back: 5
data: 00EFFC1C</code></pre><p><code>data</code>는 배열의 첫 번째 원소의 주소를 반환합니다. 그렇다는 것은 C 스타일 배열을 포인터로 접근한 것처럼 접근이 가능한 것일까요?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 5&gt; arr = { 1, 2, 3, 4, 5 };

    cout &lt;&lt; &quot;*(data + 0): &quot; &lt;&lt; *(arr.data() + 0) &lt;&lt; endl;
    cout &lt;&lt; &quot;*(data + 1): &quot; &lt;&lt; *(arr.data() + 1) &lt;&lt; endl;
    cout &lt;&lt; &quot;*(data + 2): &quot; &lt;&lt; *(arr.data() + 2) &lt;&lt; endl;
    cout &lt;&lt; &quot;*(data + 3): &quot; &lt;&lt; *(arr.data() + 3) &lt;&lt; endl;
    cout &lt;&lt; &quot;*(data + 4): &quot; &lt;&lt; *(arr.data() + 4) &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
*(data + 0): 1
*(data + 1): 2
*(data + 2): 3
*(data + 3): 4
*(data + 4): 5</code></pre><h3 id="예제-크기">예제: 크기</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 5&gt; arr = {1, 2, 3, 4, 5};

    cout &lt;&lt; &quot;empty: &quot; &lt;&lt; std::boolalpha &lt;&lt; arr.empty() &lt;&lt; endl;
    cout &lt;&lt; &quot;max_size: &quot; &lt;&lt; arr.max_size() &lt;&lt; endl;
    cout &lt;&lt; &quot;size: &quot; &lt;&lt; arr.size() &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
empty: false
max_size: 5
size: 5</code></pre><h3 id="예제-그-외">예제: 그 외</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;array&gt;

using namespace std;

int main()
{
    array&lt;int, 5&gt; arr1 = {1, 2, 3, 4, 5};

    cout &lt;&lt; &quot;=====fill before=====&quot; &lt;&lt; endl;
    for (auto item : arr1)
       cout &lt;&lt; item &lt;&lt; endl;

    cout &lt;&lt; &quot;=====fill after=====&quot; &lt;&lt; endl;
    arr1.fill(10);
    for (auto item : arr1)
        cout &lt;&lt; item &lt;&lt; endl;
    cout &lt;&lt; endl;

    array&lt;int, 5&gt; arr2 = { 6,7,8,9,0 };
    cout &lt;&lt; &quot;=====swap before=====&quot; &lt;&lt; endl;
    cout &lt;&lt; &quot;arr1&quot; &lt;&lt; &#39;\t&#39; &lt;&lt; &quot;arr2&quot; &lt;&lt; endl;
    for (size_t i = 0; i &lt; arr1.size(); i++)
       cout &lt;&lt; arr1.at(i) &lt;&lt; &#39;\t&#39; &lt;&lt; arr2.at(i) &lt;&lt; endl;

    cout &lt;&lt; &quot;=====swap after=====&quot; &lt;&lt; endl;
    arr1.swap(arr2);
    cout &lt;&lt; &quot;arr1&quot; &lt;&lt; &#39;\t&#39; &lt;&lt; &quot;arr2&quot; &lt;&lt; endl;
    for (size_t i = 0; i &lt; arr1.size(); i++)
       cout &lt;&lt; arr1.at(i) &lt;&lt; &#39;\t&#39; &lt;&lt; arr2.at(i) &lt;&lt; endl;
}</code></pre>
<pre><code>[출력 결과]
=====fill before=====
1
2
3
4
5
=====fill after=====
10
10
10
10
10

=====swap before=====
arr1    arr2
10      6
10      7
10      8
10      9
10      0
=====swap after=====
arr1    arr2
6       10
7       10
8       10
9       10
0       10</code></pre><h1 id="참고-자료">참고 자료</h1>
<p><a href="https://ko.cppreference.com/w/cpp/container/array">cpprefernce</a>
<a href="https://learn.microsoft.com/ko-kr/cpp/standard-library/array-class-stl?view=msvc-170#array">VisualC++ Docs</a>
<a href="https://modoocode.com/314">모두의 코드</a>
<a href="https://boycoding.tistory.com/213">소년코딩</a>
<a href="https://blockdmask.tistory.com/332">BlockDMask</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[STL] Standard Template Library]]></title>
            <link>https://velog.io/@whale-park18/STL-Standard-Template-Library</link>
            <guid>https://velog.io/@whale-park18/STL-Standard-Template-Library</guid>
            <pubDate>Mon, 17 Jul 2023 10:00:06 GMT</pubDate>
            <description><![CDATA[<h1 id="설명">설명</h1>
<p><strong>S</strong>tandard <strong>T</strong>emplate <strong>L</strong>ibrary란 C++에서 제공하는 표준 템플릿 라이브러리로 C++ template을 이용해 Generic(일반화: 타입에 무관한)한 프로그래밍이 가능합니다.</p>
<h2 id="stl-구성요소">STL 구성요소</h2>
<p>STL은 Container(컨테이너, 자료구조) Class, Iterator(반복자), Algorithm(알고리즘) 그리고 Function Object(함수자)라고 불리는 네 가지의 구성요소로 구성되어 있습니다.</p>
<h3 id="컨테이너">컨테이너</h3>
<p><strong>컨테이너</strong>는 객체 또는 데이터를 담는 컬렉션 또는 자료구조로 크게 순차 컨테이너, 연관 컨테이너, 비정렬 연관 컨테이너, 컨테이너 어댑터로 나눠집니다.</p>
<ul>
<li><p>순차(시퀀스) 컨테이너
  → 특별한 규칙이 없는 컨테이너, 순서가 있는 선형구조</p>
<ul>
<li>array                     : 배열</li>
<li>vector                    : 동적 배열</li>
<li>deque                     : 양방향 큐</li>
<li>list                      : 양방향 리스트</li>
<li>forward_list(C++11)       : 단방향 리스트</li>
</ul>
</li>
<li><p>연관 컨테이너
  → 특정 규칙에 의해 정렬되는 컨테이너, 순서가 없는 비선형 구조</p>
<ul>
<li>set                       : 정렬된 유일 Key 집합</li>
<li>map                       : 정렬된 유일 Key와 Value 집합</li>
<li>multiset                  : 정렬된 중복허용 Key 집합</li>
<li>multimap                  : 정렬된 중복허용 Key와 Value의 집합</li>
</ul>
</li>
<li><p>비정렬 연관 컨테이너</p>
<ul>
<li>unorder_set(C++11)        : 유일 Key 해시</li>
<li>unorder_map(C++11)        : 유일 Key와 Value의 해시</li>
<li>unorder_multiset(C++11)   : 중복허용 Key 해시</li>
<li>unorder_multimap(C++11)   : 중복허용 Key와 Value의 해시</li>
</ul>
</li>
<li><p>컨테이너 어댑터
  → 간결함과 명료성을 위해 인터페이스를 제한한 컨테이너, 반복자를 지원하지 않아 STL 알고리즘을 사용할 수 없다.</p>
<ul>
<li>stack                     : 스택</li>
<li>queue                     : 큐</li>
<li>priority_queue            : 우선순위큐</li>
</ul>
</li>
</ul>
<p><strong>순차 컨테이너</strong>의 경우 요소가 언제 삽입되는지에 따라 배열안에서 요소의 순서가 결정된다. 즉, 연속되게 저장되는 배열과 같은 구조입니다. 하지만 특이하게 <code>list</code>는 다음 요소의 주소를 가리키는 노드구조를 가지고 있습니다.</p>
<p><strong>연관 컨테이너</strong>는 어느 기준에 따라 자동으로 정렬하는 컨테이너 입니다. 따라서 순서가 없고 기준에 따라 다음 요소의 메모리 주소를 가리키는 노드구조입니다.</p>
<h3 id="반복자">반복자</h3>
<p><strong>반복자</strong>는 포인터의 개념을 품은(또는 비슷한) 객체로 컨테이너에 저장된 요소를 반복적으로 순회하여 요소를 가리키고, 그 요소에 접근해 다음 요소를 가리키게합니다. 즉, 요소의 타입에 상관없이 독립적으로 요소를 순회하는 과정을 수행할 수 있습니다.</p>
<p>반복자는 다음과 같은 특징을 가집니다.</p>
<ul>
<li>컨테이너 내부의 객체에 접근할 수 있어야 한다.
  → 참조 연산자(<code>*</code>)가 정의되어 포인터처럼 객체에 접근한다.</li>
<li>반복자는 다음 객체로 이동하고 컨테이너의모든 객체를 순회할 수 있어야 한다.
  → 증가 연산자(<code>++</code>)가 정의되어 포인터 변수처럼 증감 연산자로 다음 요소로 순회한다.</li>
<li>반복자의 위치를 판단할 수 있어야 한다.
  → <code>=</code>, <code>==</code>, <code>!=</code>와 같은 반복자간의 대입, 비교 연산자가 정의되어 있어 반복자의 위치를 파악할 수 있어야 한다.</li>
</ul>
<p>반복자는 다음과 같은 5가지로 구성되어있습니다.</p>
<ul>
<li>입력 반복자(input iterator)
  → 현재 위치의 객체의 값을 읽어 오는 반복자로, 증가 연산자를 사용하여 순방향으로만 이동할 수 있다.</li>
<li>출력 반복자(output iterator)
  → 현재 위치의 객체의 값을 변경할 수 있는 반복자로, 증가 연산자를 사용하여 순방향으로만 이동할 수 있다.</li>
<li>순방향 반복자(forward iterator)
  → 입력, 출력 반복자의 기능을 모두 가진 반복자로, 순방향으로 이동이 가능하며 재할당이 가능하다.</li>
<li>양방향 반복자(bidirectional iterator)
  → 순방향 반복자 기능에 역방향으로 이동(<code>--</code>)이 가능한 반복자이다.</li>
<li>임의 접근 반복자(random access iterator)
  → 양방향 반복자 기능과 <code>[]</code>를 사용해 임의의 요소에 접근이 가능한 반복자로 모든 컨테이너는 양방향 반복자와 임의 접근 반복자를 지원한다.</li>
</ul>
<h3 id="알고리즘">알고리즘</h3>
<p>STL에서 제공하는 알고리즘은 검색이나 정렬같은 활동을 수행하는 것으로 대부분은 반복자의 특정한 수준을 요구합니다. 알고리즘은 다음과 같은 4가지로 구성되어 있습니다.</p>
<ul>
<li>읽기 알고리즘
  → 컨테이너를 변경하지 않으며, 컨테이너의 지정된 범위에서 특정 데이터를 읽기만 하는 알고리즘, <code>algorithm</code> 헤더에 정의되어 있다.</li>
<li>변경 알고리즘
  → 컨테이너를 변경하지 않으며, 컨테이너의 지정된 범위에서 요소의 값만을 변경할 수 있는 알고리즘, <code>algorithm</code> 헤더에 정의되어 있다.</li>
<li>정렬 알고리즘
  → 컨테이너의 지정된 범위의 요소들을 정렬되도록 컨테이너를 변경하는 알고리즘, <code>algorithm</code> 헤더에 정의되어 있다.</li>
<li>수치 알고리즘
  → STL에 직접 속하지 않고 C++라이브러리로 분류되는 알고리즘으로 수치적 해석을 위해 사용, <code>numeric</code> 헤더에 정의되어 있다.</li>
</ul>
<h3 id="함수자">함수자</h3>
<p>STL은 함수 호출 연산자(<code>operator()</code>)를 오버로드하는 클래스들을 포함합니다. 이러한 클래스들의 인스턴스들은 함수자 또는 함수 객체라고 불립니다. 함수자들은 연관된 함수가 파라미터화되는 행동을 허용하고 (예를 들어 함수자의 생성자로 넘겨지는 인자를 통해서) 연관된 per-functor 상태를 함수와 함께  사용될 수 있게 유지합니다. 함수자와 함수 포인터 모두 함수 호출의 문법을 사용해서 유발될 수 있기 때문에, 이것들은 상응하는 파라미터가 오직 함수 호출 문맥에서만 보일 때 인자로서 교체될 수 있습니다.</p>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://ko.cppreference.com/w/cpp/container">cppreference</a>
<a href="https://learn.microsoft.com/ko-kr/cpp/standard-library/cpp-standard-library-overview?view=msvc-170">VisualC++ Docs</a>
<a href="https://cplusplus.com/reference/stl/?kw=stl">cpluscplus - stl</a>
<a href="https://cplusplus.com/reference/iterator/">cpluscplus - iterator</a>
<a href="https://ko.wikipedia.org/wiki/%ED%91%9C%EC%A4%80_%ED%85%9C%ED%94%8C%EB%A6%BF_%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC">위키백과</a>
<a href="https://namu.wiki/w/%ED%91%9C%EC%A4%80%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC">나무위키</a>
<a href="https://blockdmask.tistory.com/67">BlockDMask</a>
<a href="https://boycoding.tistory.com/124">소년코딩</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[New Input System]]></title>
            <link>https://velog.io/@whale-park18/New-Input-System</link>
            <guid>https://velog.io/@whale-park18/New-Input-System</guid>
            <pubDate>Sun, 16 Jul 2023 07:35:57 GMT</pubDate>
            <description><![CDATA[<h1 id="설명">설명</h1>
<p>New Input System은 다양한 기기와 플랫폼 전반에 걸친 사용 편의성과 일관성을 초점으로 개발된 새로운 입력 시스템으로 기존의 입력 시스템의 불편한 점이 개선됐습니다.</p>
<p>기존의 입력 시스템은 지금처럼 많은 플랫폼과 기기를 지원하기 이전에 설계되어 사용자 친화적이지 못했습니다. 때로는 파일을 실행한 후 컨트롤러를 연결하는 것과 같은 간단한 작업도 원할하게 처리하지 못하는 경우도 발생했습니다. 따라서 입력 시스템을 완전히 새로 구축하게 된 것입니다.</p>
<h2 id="사용-편의성">사용 편의성</h2>
<p>입력 시스템의 새로운 워크플로는 간단한 인터페이스로 모든 플랫폼을 지원하며, 커스텀 기기 혹은 출시 예정인 기기까지 쉽게 확장 가능합니다.</p>
<p>액션(Action) 중심의 워크플로는 게임 코드와 상호 작용하는 논리적 입력과 사용자가 수행하는 물리적 액션을 구분하도록 설계되었습니다. 전용 에디터 또는 스크립트에서 액션을 정의하고, 이를 기기의 주요 동작 또는 마우스 왼쪽 버튼과 같이 추상적이거나 구체적인 입력에 모두 연동시킬 수 있습니다.</p>
<p><img src="https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/10/image4-1.png?imwidth=1920&h=fa6290f1&itok=2BlgQIMU" alt="Action Maps">
[출처: <a href="https://blog.unity.com/kr/technology/introducing-the-new-input-system">유니티 블로그</a>]</p>
<p>입력 시스템의 <strong>PlayerInput</strong> 컴포넌트를 사용하면, 게임 내 플레이어 수에 관계 없이 입력 액션을 GameObject 및 스크립트 액션 반응에 쉽게 연결할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/14ad877a-e259-4c3e-8859-1301d7e617b0/image.png" alt="PlayerInput 컴포넌트"></p>
<h2 id="new-input-system-사용하기">New Input System 사용하기</h2>
<h3 id="1-inputactions-애셋-생성">1. InputActions 애셋 생성</h3>
<p>기존의 입력 시스템은 프로그래머가 스크립트에 각각의 플랫폼에 맞는 API를 호출해 입력 여부를 확인했습니다. 하지만 새로운 입력 시스템은 애셋 형태로 관리되며 이 애셋은 <code>InputActions</code> 애셋이라 불립니다.</p>
<p><code>InputActions</code> 애셋은  <code>마우스 우클릭/생성/Input Actions</code>로 생성할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/0ade1175-81c5-4b31-a91e-0fd09e6ba6ec/image.png" alt="Create Input Actions"></p>
<h3 id="2-액션-에디터-사용">2. 액션 에디터 사용</h3>
<p>생성된 애셋을 더블 클릭하면 액션 에디터가 열립니다. 이 액션 에디터에서 모든 입력 방법과 입력에 대응하는 액션을 관리합니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/4ce8b138-3f24-41d8-bc89-c3336e66f4c7/image.png" alt="OpenActionEditor"></p>
<h3 id="3-inputactions-애셋-설정하기">3. InputActions 애셋 설정하기</h3>
<p>이제 InputActions 애셋을 설정해보겠습니다. 크게 설정해야 하는 것은 <strong>Action Maps</strong>와 <strong>Actions</strong> 두 가지입니다.</p>
<h4 id="31-action-maps">3.1. Action Maps</h4>
<p><strong>Action Maps</strong>는 작업의 명명된 컬렉션입니다. 즉, 액션들의 집합이라 하며 각각의 요소들을 <code>InputActionMap</code>이라 부릅니다. &#39;캐릭터 조작&#39;, &#39;UI 조작&#39; 등을 예로 들 수 있습니다.</p>
<p>이 글에서는 &#39;캐릭터 조작&#39;을 예제를 들어보겠습니다. 눈치 빠른 분들은 이미 눈치 채셨겠지만 액션 에디터의 Action Maps 카테고리의 오른쪽 상단에 더하기 버튼이 있습니다. 이 더하기 버튼을 누르면 새로운 InputActionMap이 생성됩니다. 이름은 &quot;PlayerControl&quot;로 지정하겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/df41d8f7-34f2-43c4-ae69-e156df1e7b5b/image.png" alt="CreateInputActionMap"></p>
<blockquote>
<p>InputActionMap은 최소 하나 이상의 액션을 담고있어야 하기 때문에 자동으로 한 개의 Action이 생성됩니다.</p>
</blockquote>
<blockquote>
<p>New Action 왼쪽에 삼각형 토글을 눌러 출력된 <code>&lt;No Binding&gt;</code> 항목을 삭제합니다. 자세한 설명은 이후에 하겠습니다.</p>
</blockquote>
<h4 id="32-actions">3.2. Actions</h4>
<p><strong>Actions</strong>는 행동을 정의하고 키를 바인딩합니다. 각각이 요소를 <code>InputAction</code>이라 부릅니다. &#39;<strong>이동</strong>이라는 행동은 W,A,S,D로 입력받다&#39;를 예로 들 수 있습니다.</p>
<p>&#39;캐릭터 조작&#39; 중에서 이동에 대해 다음과 같이 정의했습니다.</p>
<ul>
<li>이동: W, A, S, D</li>
</ul>
<p>InputAction을 생성하는 방법은 InputActionMap을 생성하는 방법과 동일합니다. 먼저 이동에 대해 설정하겠습니다. InputActionMap을 생성하며 같이 생성된 InputAction의 이름을 Move로 변경하겠습니다.</p>
<p>Action Properties의 액션/Action Type을 <code>값</code>으로 변경합니다. 값으로 변경하게 되면 아래의 Initial State Check 속성 대신 Control Type 속성이 생기게 됩니다. 이 값을 <code>Vector2</code>로 변경합니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/165fda56-82db-4200-b9ff-e503812e30ad/image.png" alt="ChangeActionTypeButtonToValue"></p>
<blockquote>
<p>이 Action Type은 어떠 것을 기준으로 키 입력이 됐는지 안됐는지를 정하는 옵션입니다.</p>
<ul>
<li>Value: 입력에 따른 값 반환(예. <code>+</code>: 오른쪽, <code>-</code>: 왼쪽)</li>
<li>Button: 버튼이 눌렸는 지, 안눌렸는지에 따라 값 반환(예. 점프)</li>
</ul>
</blockquote>
<blockquote>
<p>Control Type은 해당 키가 눌렀을 때, 스크립트에서 어떤 타입으로 반환받을 지, 설정하는 옵션이다. 이동의 경우, 바닥이라는 2차원을 이동하는 것이기 때문에 <code>Vector2</code>로 설정합니다.</p>
</blockquote>
<p>이게 행동에 대한 키를 바인딩 해보겠습니다. Move의 오른쪽을 보면 더하기 버튼이 보입니다. 버튼을 누르면 바인딩 목록 중에 <code>Add Up\Down\Left\Right Composite</code> 항목을 선택하고 이름을 WASD로 설정합니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/aa839a88-3a2d-4171-91e3-1a9898e4ce70/image.png" alt="CreateActionBinding"></p>
<p>잠깐 정리하는 시간을 가져보겠습니다. 액션 에디터를 보면 각 항목들의 왼쪽에 노란색, 초록색, 파란색, 빨간색을 볼 수 있습니다. 각각의 색이 의미하는 것은 다음과 같습니다.</p>
<ul>
<li>노란색: InputActionMap</li>
<li>초록색: InputAction</li>
<li>파란색: 바인딩 집합</li>
<li>빨간색: 바인딩 설정</li>
</ul>
<blockquote>
<p>유니티에서 InputActionMap, InputAction같은 명칭을 설명한 것은 있지만 아닌 것도 있어 색으로 잠시 정리해봤습니다.</p>
</blockquote>
<p>이제 바인딩을 설정해보겠습니다. WASD의 항목 중 하나를 눌러보면 Binding의 Path 항목을 볼 수 있습니다. Path이라는 이름만 보면 경로를 생각하게 되지만 바인딩할 <strong>키</strong>를 의미합니다. </p>
<p>WASD란 이름에 맞게 UP: W, Down: S, Left: A, Right: D로 설정합니다. </p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/4461fa36-d290-4e29-84a5-529a8c474160/image.png" alt="Binding"></p>
<p>키 입력에 대한 한 가지 팁을 드리자면 Path을 눌렀을 때, 왼쪽 상단의 <strong>Listen</strong> 버튼을 눌르면 입력 받은 키가 출력됩니다. 맞으면 해당 키를 선택하면 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/0ab136d6-1ca0-4483-8af4-1efb2acfaf39/image.png" alt="Binding_Path"></p>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://blog.unity.com/kr/technology/introducing-the-new-input-system">Unity Blog</a>
<a href="https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/ActionAssets.html">Unity Manual</a>
<a href="https://daekyoulibrary.tistory.com/entry/Unity-New-Input-System-1">Dibrary</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LineRenderer를 이용한 라인, 정다각형 그리기]]></title>
            <link>https://velog.io/@whale-park18/LineRenderer%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%8B%A4%EA%B0%81%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@whale-park18/LineRenderer%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%8B%A4%EA%B0%81%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Fri, 14 Jul 2023 19:44:15 GMT</pubDate>
            <description><![CDATA[<h1 id="설명">설명</h1>
<p><code>Line Renderer</code> 컴포넌트는 3D 공간에서 두 개 이상의 <u>지점 배열</u>을 가져와 <strong>각각의 점 사이에 직선을 그리는 컴포넌트</strong>입니다. 라인 렌더러를 사용하여 간단한 직선에서부터 파형, 다각형 등의 복잡한 나선에 이르기까지 모든 것을 그릴 수 있습니다. 다만 그려지는 모든 라인은 <strong>연속적</strong>이어야 합니다. 두 개 이상의 완전히 분리된 선을 그려야하는 경우 라인 렌더러를 포함한 <u>여러 개의 게임 오브젝트</u>를 사용해야 합니다.</p>
<p>라인 렌더러는 픽셀 단위 너비의 라인같은 가는 선은 렌더링하지 않고 월드 공간 단위 너비의 폴리곤을 렌더링합니다. 또한 트레일 렌더러와 동일한 알고리즘을 라인 렌더링에 사용합니다.</p>
<h2 id="라인-설정">라인 설정</h2>
<h2 id="라인-그리기">라인 그리기</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/1acb531c-18eb-4241-a0d5-d9a67e893ffd/image.png" alt="LineOutput"></p>
<p>LineRenderer 컴포넌트는 Position 프로퍼티의 위치를 기반으로 인덱스 순서대로 라인을 연결합니다. <code>positionCount</code> 메소드로 Position의 개수를 설정한 후, <code>SetPosition</code> 메소드로 Position의 위치를 설정하면 됩니다.</p>
<pre><code class="language-cs">using UnityEngine;

[RequreComponent(typeof(LineRenderer))]
public class LineGenerator : Monobehaviour
{
    private LineRenderer _lineRenderer;

    private void Awake()
    {
        _lineRenderer = GetComponent&lt;LineRenderer&gt;();
    }

    public void StopDraw()
    {
        _lineRenderer.enable = false;
    }

    /// &lt;summary&gt;
    /// start와 end를 잇는 라인을 그리는 메소드
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;start&quot;&gt;시작점&lt;/param&gt;
    /// &lt;param name=&quot;end&quot;&gt;끝점&lt;/param&gt;
    public DrawLine(Vector3 start, Vector3 end)
    {
        if (_lineRenderer.enabled == false)
                _lineRenderer.enabled = true;
        _lineRenderer.positionCount = 2;
        _lineRenderer.loop = false;

        _lineRenderer.SetPosition(0, start);
        _lineRenderer.SetPosition(1, end);
    }

    /// &lt;summary&gt;
    /// Positions의 정보로 라인을 그리는 메소드
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;positions&quot;&gt;&lt;/param&gt;
    public void DrawLine(Vector3[] positions)
    {
        if (_lineRenderer.enabled == false)
            _lineRenderer.enabled = true;
        _lineRenderer.positionCount = positions.Length;
        _lineRenderer.loop = false;

        for(int i = 0; i &lt; positions.Length; i++)
        {
            _lineRenderer.SetPosition(i, positions[i]);
        }
    }
}</code></pre>
<h2 id="정다각형-그리기">정다각형 그리기</h2>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/631a80e9-f2bb-4f8b-b019-e162d72f8eea/image.png" alt="PolygonOutput"></p>
<h3 id="다각형-꼭지점">다각형 꼭지점</h3>
<p><img src="https://velog.velcdn.com/images/whale-park18/post/afd58ead-e0e7-48cd-ba97-430c7861f20c/image.jpg" alt="PolygonInCycle"></p>
<p>다각형의 특징은 원에 내접하며 각각의 내접한 꼭지점과 원점의 거리가 모두 같은 특징을 지닙니다. 이를 이용해 정다각형을 그릴 수 있고 크게 2가지 방법이 있습니다.</p>
<ol>
<li>Vector3 X Rotation을 이용한 좌표 계산</li>
<li>삼각 함수를 이용한 좌표 계산</li>
</ol>
<p>첫 번재 방법은 정말 간단합니다. 초기값 $P_0$(0, 반지름)을 구한 다음, <code>Quaternion.AngleAxis</code> 메소드를 곱하면 $\theta$만큼 회전된 위치 값이 나옵니다.</p>
<blockquote>
<p><strong>주의점</strong>
&#39;Vector3 X Rotation&#39;이라 제목을 붙였지만 계산 순서는 <strong>Rotation X Vector3</strong> 순입니다. 역순으로 계산할 경우 에러가 발생합니다.</p>
</blockquote>
<p>두 번째 방법은 약간의 수학 지식이 필요합니다.
<img src="https://velog.velcdn.com/images/whale-park18/post/96f5f51d-63e6-4fed-81a7-a092e5bff4cd/image.jpg" alt="AngleFunctionToPoint">
반지름이 1일 때, $Sin(\theta)$, $Cos(\theta)$의 점 $P$의 좌표 값이 구해집니다. 즉, $x = Cos(\theta) * R$, $y = Sin(\theta) * R$ 이라는 것을 알 수 있습니다. 유니티에서는 <code>Mathf</code> 구조체에 게임 및 앱 개발에 일반적으로 필요한 삼각 함수, 로그 함수, 기타 함수를 필요한 일반적인 함수 컬렉션을 제공하기 때문에 이를 이용하면 쉽게 구현할 수 있습니다.</p>
<blockquote>
<p><strong>주의점</strong>
<code>Mathf</code>에서 제공하는 삼각 함수의 매개 변수는 라디안을 기준으로 작동합니다. <code>angle * Mathf.Deg2Rad</code>를 계산하면 각도가 라디안으로 변환됩니다(반대로 라디안 -&gt; 각도로 변환할 땐, <code>Mathf.Rad2Deg</code>를 곱하면 됩니다).</p>
</blockquote>
<pre><code class="language-cs">public class PolygonPositionsGenerator
{
    /// &lt;summary&gt;
    /// Vector3 x Rotation 기반 Position 계산하는 메소드
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;numberOfPolygonPoint&quot;&gt;다각형 꼭지점 개수&lt;/param&gt;
    /// &lt;param name=&quot;radius&quot;&gt;반지름&lt;/param&gt;
    /// &lt;returns&gt;다각형 꼭지점 위치&lt;/returns&gt;
    public static Vector3[] GetPolygonPoisitionsVectorMulRotation(int numberOfPolygonPoint, float radius)
    {
        List&lt;Vector3&gt; positions = new List&lt;Vector3&gt;();
        float angle = 360f / numberOfPolygonPoint;

        positions.Add(new Vector3(0, 0, radius));
        for(int i = 1; i &lt; numberOfPolygonPoint; i++)
        {
            positions.Add(Quaternion.AngleAxis(angle, Vector3.up) * positions[i - 1]);
        }

        return positions.ToArray();
    }

    /// &lt;summary&gt;
    /// 삼각 함수 기반 Position 계산하는 메소드
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;numberOfPolygonPoint&quot;&gt;다각형 꼭지점 개수&lt;/param&gt;
    /// &lt;param name=&quot;radius&quot;&gt;반지름 &lt;/param&gt;
    /// &lt;returns&gt;다각형 꼭지점 위치&lt;/returns&gt;
    public static Vector3[] GetPolygonPositionsAngleFunction(int numberOfPolygonPoint, float radius)
    {
        List&lt;Vector3&gt; positions = new List&lt;Vector3&gt;();
        float angle = 360f / numberOfPolygonPoint;
        float currentAngle = 0f;

        for (int i = 0; i &lt; numberOfPolygonPoint; i++)
        {
            float radian = Mathf.Deg2Rad * currentAngle;

            float x = Mathf.Cos(radian) * radius;
            float z = Mathf.Sin(radian) * radius;
            currentAngle += angle;

            positions.Add(new Vector3(x, 0, z));
        }

        return positions.ToArray();
    }
}</code></pre>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://docs.unity3d.com/kr/2021.2/Manual/class-LineRenderer.html">Unity Documentation</a>
<a href="https://www.youtube.com/watch?v=wAY9exBIw3A">고박사</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] nullptr]]></title>
            <link>https://velog.io/@whale-park18/C-nullptr</link>
            <guid>https://velog.io/@whale-park18/C-nullptr</guid>
            <pubDate>Wed, 12 Jul 2023 18:34:08 GMT</pubDate>
            <description><![CDATA[<h1 id="설명">설명</h1>
<p><code>nullptr</code> 키워드는 null 포인터를 나타내는 상수 리터럴로 <code>std::nullptr_t</code> 타입의 prvalue입니다. <code>nullptr</code> 키워드는 헤더를 포함하지 않고 사용할 수 있지만 <code>std::ptr_t</code> 타입을 사용하려면 <code>&lt;cstddef&gt;</code> 헤더를 포함해야 합니다.</p>
<blockquote>
<p><code>std::nullptr_t</code> 타입은 암시적으로 모든 타입의 포인터로 형 변환이 가능합니다.</p>
</blockquote>
<h2 id="null의-문제">NULL의 문제</h2>
<p>C++에 객체지향 패러다임이 적용되면서 오버로딩을 지원하게 되면서 <code>NULL</code>을 사용할 때, 문제가 발생하게 됩니다.</p>
<blockquote>
<p>오버로딩을 간단하게 설명하면 똑같은 이름을 사용하지만 다른 매개 변수를 갖는 함수를 뜻합니다.</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

void func(int n)
{
    cout &lt;&lt; &quot;call void func(int)&quot; &lt;&lt; endl;
}

void func(int* p)
{
    cout &lt;&lt; &quot;call void func(int*)&quot; &lt;&lt; endl;
}

int main()
{
    func(0);
    func(NULL);
    func(nullptr);
}</code></pre>
<pre><code>[출력 결과]
call void func(int)
call void func(int)
call void func(int*)</code></pre><p>프로그래머는 <code>NULL</code>을 전달하며 <code>void func(int*)</code>가 호출되는 것을 의도했겠지만 <code>NULL</code>은 <code>0</code>으로 정의된 매크로이기 때문에 <code>void func(int)</code>가 호출됩니다. 하지만 <code>nullptr</code>을 전달할 경우 의도대로 <code>void func(int*)</code>가 호출되는 것을 볼 수 있습니다. </p>
<p>포인터를 초기화할 때, <code>nullptr</code> 키워드와 <code>NULL</code>은 똑같이 <code>0</code>으로 초기호되지만, 매개 변수로 전달될 때, <code>NULL</code>은 <code>0</code>으로 정의된 상수이기 때문에 컴파일러가 <code>void func(int)</code>로 호출하지만 <code>nullptr</code>은 <code>std::nullptr_t</code> 타입의 상수 리터럴이기 때문에 <code>void func(int*)</code>가 호출되는 것입니다.</p>
<h2 id="nullptr을-사용해야-하는-이유">nullptr을 사용해야 하는 이유</h2>
<p><code>nullptr</code> 키워드를 사용하면 위의 상황처럼 의도치 못한 오류의 가능성을 없애 안전성을 높일 뿐만 아니라 코드의 가독성까지 높이기 때문에 C++11 이후의 버전에서 <code>nullptr</code> 키워드 대신 <code>NULL</code>을 사용해야 할 이유가 없습니다.</p>
<blockquote>
<p><code>NULL</code>을 비어있다라는 의미로 초기화 하는 경우도 있기 때문에 <code>NULL</code>을 보고 null 포인터라고 판단할 수 없지만, <code>nullptr</code> 키워드는 null 포인터임을 알 수 있어 코드의 가독성이 높아집니다.</p>
</blockquote>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://en.cppreference.com/w/cpp/language/nullptr">cppreference</a>
<a href="https://learn.microsoft.com/ko-kr/cpp/cpp/nullptr?view=msvc-170">VisualC++ Docs</a>
<a href="https://blockdmask.tistory.com/501">BlockDMask</a>
<a href="https://psychoria.tistory.com/21">냉정과 열정 사이</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] Range-for 문]]></title>
            <link>https://velog.io/@whale-park18/C-Range-for-%EB%AC%B8</link>
            <guid>https://velog.io/@whale-park18/C-Range-for-%EB%AC%B8</guid>
            <pubDate>Wed, 12 Jul 2023 10:01:10 GMT</pubDate>
            <description><![CDATA[<h1 id="구문">구문</h1>
<blockquote>
<p><code>for</code> <code>(</code> <code>element-type</code> <code>element-name</code> <code>:</code> <code>data-list</code> <code>)</code></p>
</blockquote>
<ul>
<li><code>element-type</code> - <code>data-list</code>의 요소의 데이터 타입</li>
<li><code>element-name</code> - <code>data-list</code>의 요소에 접근할 변수명</li>
<li><code>data-list</code> - 배열, <code>vector</code> 등의 순회가 가능한 데이터 리스트</li>
</ul>
<h1 id="설명">설명</h1>
<p>배열, 벡터 등의 데이터 리스트 자료형을 순회해야 할 때, 보통 인덱스와 데이터 리스트의 길이를 이용합니다. 정확한 인덱스를 설정했다면 문제없지만 프로그래머의 실수로 잘못된 인덱스를 설정한다면 에러가 발생하게 됩니다.</p>
<p>범위기반 for 문은 기존의 for 문과 달리 스스로 데이터 리스트의 처음부터 끝까지 순회하는 반복문입니다. 스스로 순회 범위를 판단하기 때문에 인덱스 실수로 인한 에러를 줄일 수 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int main()
{
    // 1. 배열
    int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    for(auto element : arr)
    {
        cout &lt;&lt; element &lt;&lt; endl;
    }

    // 2. STL
    vector&lt;int&gt; v = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    for(auto element : v)
    {
        cout &lt;&lt; element &lt;&lt; endl;
    }
}</code></pre>
<p>범위기반 for 문에서 접근한 요소는 값을 복사한 것입니다. 때문에 범위기반 for 문의 본문에서 요소를 변경하더라도 데이터 리스트에는 적용되지 않습니다. 또한 요소의 크기가 크다면 복사로 인한 성능 하락이 발생할 수도 있습니다.</p>
<p>범위기반 for 문에서 접근한 요소를 변경하고 복사로 인한 성능 하락을 피하는 방법은 간단합니다. 참조를 이용하는 것입니다. </p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int main()
{
    vector&lt;int&gt; v = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    for(auto&amp; element : v)
    {
        element += 1;
    }

    for(auto&amp; element : v)
    {
        cout &lt;&lt; element &lt;&lt; endl;
    }
}</code></pre>
<p>하지만 범위기반 for 문은 아이러니하게도 인덱스 때문에 기존의 for 문을 완전히 대체하지 못합니다. 범위기반 for문에는 인덱스를 나타내는 정보가 존재하지 않고 오직 요소의 정보만을 표시합니다. 때문에 인덱스를 분류나 연산을 할 수 없습니다.</p>
<p>굳이 하려면 할 수 있습니다. 하지만... &#39;이럴거면 그냥 인덱스에 신경써서 기존의 for 문을 쓰는게 낮지 않나...?&#39; 라는 생각이 들긴합니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

int main()
{
    int arr[] = { 1, 2, 3, 4 };
    int index = 0;
    for(auto&amp; element : arr)
    {
        element += index++ * 2;
        cout &lt;&lt; element &lt;&lt; endl;
    }
}</code></pre>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/range-based-for-statement-cpp?view=msvc-170">VisualC++ Docs</a>
<a href="https://blockdmask.tistory.com/319">BlockDMask</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] lambda]]></title>
            <link>https://velog.io/@whale-park18/lambda-expression-lamda</link>
            <guid>https://velog.io/@whale-park18/lambda-expression-lamda</guid>
            <pubDate>Tue, 11 Jul 2023 17:17:44 GMT</pubDate>
            <description><![CDATA[<h1 id="구문">구문</h1>
<blockquote>
<p><code>[</code> <code>capture</code> <code>]</code> <code>(</code> <code>params</code> <code>)</code> <code>[:specifiers]</code> <code>[:exception]</code> <code>[:trailing return type]</code> <code>{</code> <code>body</code> <code>}</code></p>
</blockquote>
<ol>
<li><code>capture</code> - 캡처</li>
<li><code>params</code> - 매개 변수</li>
<li><code>[:specifiers]</code> - 변경 가능한 사양</li>
<li><code>[:exception]</code> - 예외 사양</li>
<li><code>[:trailing return type]</code> - 후위 반환 형식</li>
<li><code>body</code> - 본문</li>
</ol>
<h1 id="설명">설명</h1>
<p>lambda expression는 람다(lambda)라고 많이 불리며 함수 객체를 정의하는 편리한 방법입니다. 즉, 익명 함수를 뜻합니다. 람다 식은 일반적으로 알고리즘 또는 비동기 함수에 전달되는 몇 줄의 코드를 캡슐화하는데 사용됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;cmath&gt;

using namespace std;

void absSort(vector&lt;int&gt;&amp; origin)
{
    sort(origin.begin(), origin.end(),
        // 람다 식을 이용한 커스텀 Compare
        [](int left, int right) {
            return abs(left) &lt; abs(right);
        }
    );
}

int main()
{
    vector&lt;int&gt; values = { -5, 4, 3, -2, 1 };
    absSort(values);
    for (auto value : values)
    {
        cout &lt;&lt; value &lt;&lt; endl;
    }
}</code></pre>
<h2 id="capture">Capture</h2>
<p>람다는 캡처를 이용해, 람다 외부에 정의되어 있는 변수나 상수를 람다 내부에서 사용할 수 있습니다. &#39;매개 변수로 전달해 사용하면 되는 것 아닌가?&#39; 의문이 들 수 있습니다.</p>
<p>외부에 있는 모든 변수와 상수에 대한 매개 변수를 정의할 수 없으며 STL을 사용할 경우, 제약이 발생할 수 있습니다. 때문에 이를 방지하기 위해 람다 내부와 소통할 수 있는 캡처 절을 제공하는 것입니다.</p>
<p>람대 외부에 정의된 변수나 상수를 캡처하는 방식으로 <strong>call-by-vaule</strong>, <strong>call-by-reference</strong> 두 가지가 존재합니다. 즉, 값으로 캡처하는가, 참조로 캡처하는가 차이입니다.</p>
<p>캡처 절에서는 다음과 같은 5가지 형태를 제공합니다(외부 변수 x, y, z가 존재).</p>
<ol>
<li><code>[=]</code> - 외부의 모든 변수나 상수들(x, y, z)을 값으로 가져온다.</li>
<li><code>[&amp;]</code> - 외부의 모든 변수나 상수들(x, y, z)을 참조로 가져온다.</li>
<li><code>[=, &amp;x]</code> - 외부의 모든 변수를나 상수들(y, z)을 값으로 가져오지만, x는 참조로 가져온다.</li>
<li><code>[&amp;, y]</code> - 외부의 모든 변수나 상수들(x, z)을 참조로 가져오지만, y는 값으로 가져온다.</li>
<li><code>[&amp;y, z]</code> - 선택한 변수나 상수(y z)를 지정한 방식(y: 참조, z: 값)에 따라 가져온다.</li>
</ol>
<blockquote>
<p>참조로 복사한 외부 변수는 대입 연산자를 통해 값을 변경할 수 있지만, 값으로 복사한 외부 변수는 대입 연산자를 통해 값을 변경할 수 없습니다.</p>
</blockquote>
<h2 id="params">params</h2>
<p>람다는 결국 함수이기 때문에 매개 변수가 존재할 수 있습니다. 일반적인 함수들 처럼 매개 변수의 존재 여부에 상관 없이 소괄호(<code>()</code>)를 사용해야 하지만 람다에서 매개 변수가 없을 경우, 소괄호를 생략할 수 있습니다.</p>
<pre><code class="language-cpp">// 매개 변수 X
[]() { cout &lt;&lt; &quot;매개 변수 X&quot; &lt;&lt; endl; }();
[] { cout &lt;&lt; &quot;매개 변수 X&quot; &lt;&lt; endl; }();

// 매개 변수 O
[](int a, int b) { cout &lt;&lt; a + b &lt;&lt; endl; }(1, 3);</code></pre>
<blockquote>
<p>람다 식을은 인수로 전달하거나 <code>auto</code> 키워드로 함수 포인터를 지정해 사용하지 않아도 본문을 정의한 중괄호(<code>{}</code>) 뒤에 소괄호(<code>()</code>)에 매개 변수를 전달하면 람다 식이 실행됩니다. </p>
</blockquote>
<h2 id="specifiers">Specifiers</h2>
<p>일반적으로 람다 식의 호출 연산자는 const-by-value이지만 <code>mutable</code> 키워드를 사용하면 이를 취소합니다. </p>
<h2 id="exception">exception</h2>
<p><code>noxcept</code> 키워드를 사용하여 람다 식이 예외를 throw하지 않음을 나타낼 수 있습니다. 일반 함수와 마찬가지로 람다 식이 <code>noexcept</code> 예외 사양을 선언하고 람다 본문이 예외를 throw하는 경우 에러가 발생합니다.</p>
<pre><code class="language-cpp">int main()
{
    [] () noexcept { throw 5; } (); // error!
}</code></pre>
<h2 id="trailing-return-type">trailing return type</h2>
<p>람다 식의 반환 형식은 자동으로 추론되기 때문에 후행 반환 형식(trailing return type)을 지정하지 않아도 됩니다. 후행 반환 형식을 지정하고 싶은 경우 중괄호(<code>{}</code>) 앞에 <code>-&gt;</code>(후행 반환 형식) 키워드를 작성해 지정합니다.</p>
<pre><code class="language-cpp">[] { return 100; }();
[] () -&gt; int { return 200; }();</code></pre>
<h2 id="body">body</h2>
<p>람다 식의 본문은 일반 함수 또는 멤버 함수의 본문에 허용되는 모든 항목을 포함할 수 있습니다. 일반 함수와 람다 식 모두의 본문은 다음고 같은 종류의 변수에 접근할 수있습니다.</p>
<ul>
<li>캡처 절에 의해 캡처된 변수</li>
<li>매개 변수</li>
<li>로컬 변수</li>
<li>클래스 내에서 선언되고 캡처되는 경우 클래스 <code>this</code> 데이터 멤버</li>
<li>정적 스토리지 기간에 있는 모든 변수(예: 전역 변수)</li>
</ul>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://en.cppreference.com/w/cpp/language/lambda">cppreference</a><br><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170">VisualC++ Docs</a><br><a href="https://blockdmask.tistory.com/491">BlockDMask</a><br><a href="https://modoocode.com/196">모두의 C++</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] auto]]></title>
            <link>https://velog.io/@whale-park18/keyward-auto</link>
            <guid>https://velog.io/@whale-park18/keyward-auto</guid>
            <pubDate>Mon, 10 Jul 2023 19:19:31 GMT</pubDate>
            <description><![CDATA[<h1 id="구문">구문</h1>
<blockquote>
<p><code>auto</code> <code>variable-name</code> = <code>initializer</code> <code>;</code>
<code>auto</code> <code>variable-name</code> = <code>lambda expression|Function Pointer</code> <code>;</code></p>
</blockquote>
<ul>
<li><code>[variable-name]</code> - 식별자</li>
<li><code>initializer</code> - 초기화 할 값</li>
<li><code>expression</code> - 람다식</li>
</ul>
<h1 id="설명">설명</h1>
<p><code>auto</code> 키워드는 C++11 이전에는 다른 의미로 사용됐지만, 이후에는 컴파일러가 선언된 변수 또는 람다 식 매개 변수의 초기화 식을 사용하여 형식을 추록하도록 지시하는 키워드입니다.</p>
<p>즉, Java, C#의 &#39;var&#39; 키워드 처럼 초기화 값에 따라 타입을 정해주는 <u><strong>타입 추론</strong></u> 키워드입니다. <code>char</code>, <code>int</code> 등의 기본 타입형과 <code>struct</code>, <code>class</code> 등의 타입까지 추론 가능합니다. 또한 함수 포인터나 <u>함수 자체(람다 함수)</u>도 될 수 있습니다.</p>
<p>다음과 같은 이점을 제공하기 때문에 Visual C++에서는 대부분의 상황에서 <code>auto</code> 키워드를 사용하는 것을 권장하고 있습니다.</p>
<ul>
<li>경고성 - 함수의 반환 형식이 변경되는 경우를 포함하여 식의 형식이 변경되어도 작동한다.</li>
<li>성능 - 변환이 없음을 보장한다.</li>
<li>유용성 - 형식 이름 맞춤법 오류 및 오타에 대한 걱정이 없다.</li>
<li>효율성 - 코딩이 더 효율적이다.</li>
</ul>
<p>하지만 <code>auto</code> 키워드가 어느 곳에서나 사용되는 만능인 것은 아닙니다. 다음과 같은 제약사항이 존재합니다.</p>
<ul>
<li><code>auto</code> 키워드는 다른 형식 지정자(<code>int</code> 등)와 결합할 수 없다.</li>
<li><code>auto</code> 키워드가 선언된 곳에는 이니셜라이저가 있어야 한다.</li>
<li>함수의 매개변수로 사용될 수 없다.</li>
<li>초기화 하기 전에 사용할 수 없다.</li>
<li><code>auto</code> 키워드 선언된 형식으로 캐스팅할 수 없다.</li>
<li><code>auto</code> 키워드에 <code>sizeof</code> 및 <code>typeid</code> 연산자를 사용할 수 없다.</li>
<li>구조체나 클래스 등의 멤버 변수로 사용할 수 없다.</li>
</ul>
<blockquote>
<p>Visual Studio 기준으로 <code>auto</code>로 선언한 변수명 위에 마우스를 가져다 대면 추론된 타입을 확인할 수 있습니다.</p>
</blockquote>
<p><strong>예) 값</strong></p>
<pre><code class="language-cpp">auto type1 = 10;            // int
auto type2 = 10.f;            // float
auto type3 = &#39;c&#39;;            // char
auto type4 = &quot;c&quot;;            // char const *
auto type5 = &quot;String&quot;;        // char const *
auto type6 = { 1, 2, 3 };    // std::initializer_list&lt;int&gt;</code></pre>
<p><code>auto</code> 키워드는 C++11에서는 함수의 반환형으로 사용할 수 있지만 후행 반환 형식을 작성했을 경우에만 사용 가능합니다. 하지만 C++14 이상 부터는 제약없이 사용 가능합니다.</p>
<p><strong>예) 함수</strong></p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;numeric&gt;

using namespace std;

void increase(int value)
{
    cout &lt;&lt; value + 1 &lt;&lt; endl;
}

// `-&gt; int`: 후행 반환 형식
// C++14 버전 이상이라면 작성하지 않아도 됨.
auto sum(vector&lt;int&gt; origin) -&gt; int
{
    return accumulate(origin.begin(), origin.end(), 0);
}

int main()
{
    // 함수 포인터
    auto Increase = increase;

    // 람다 함수
    vector&lt;int&gt; indexList = { 1, 2, 3, 4, 5 };
    auto printIndexList = [&amp;] {
        for (const auto&amp; index : indexList)
            cout &lt;&lt; index &lt;&lt; endl;
    };

    Increase(5);
    printIndexList();
    cout &lt;&lt; sum(indexList) &lt;&lt; endl;
}</code></pre>
<h2 id="--const">*, &amp;, const</h2>
<p>기본적으로 <code>auto</code> 키워드는 일반 타입 키워들과 동일한 문법을 공유하기 때문에 <code>auto</code> 키워드 앞과 뒤에 붙여서 사용할 수 있습니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int main()
{
    const auto target = 3;
    vector&lt;int&gt; indexList = { 1, 2, 3, 4, 5 };

    // 범위 기반 for문
    for(auto&amp; index : indexList)
    {
        if(index &gt;= target)
        {
            cout &lt;&lt; index &lt;&lt; endl;
        }
    }
}</code></pre>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://en.cppreference.com/w/cpp/keyword/auto">cppreference</a>
<a href="https://learn.microsoft.com/ko-kr/cpp/cpp/auto-cpp?view=msvc-170">VisualC++ Docs</a>
<a href="https://blockdmask.tistory.com/384">BlockDMask</a>
<a href="https://blog.naver.com/kyed203/220068115571">memOry</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C++] enum class]]></title>
            <link>https://velog.io/@whale-park18/keyward-enum-class</link>
            <guid>https://velog.io/@whale-park18/keyward-enum-class</guid>
            <pubDate>Mon, 10 Jul 2023 09:32:02 GMT</pubDate>
            <description><![CDATA[<h1 id="구문">구문</h1>
<blockquote>
<p>scoped enum:
<code>enum</code> <code>[class|struct]</code> <code>[variable-name]</code> <code>[:type]</code> <code>{</code> <code>[enum-list]</code> <code>}</code> <code>;</code></p>
</blockquote>
<ul>
<li><code>[class|struct]</code> - 열거형의 범위(scope)를 지정</li>
<li><code>[variable-name]</code> - 열거형에 지정된 형식 이름</li>
<li><code>[:type]</code> (옵션, defalut: <code>int</code>) - 열거형의 기본 타입, 모든 열거자는 동일한 기본 형식을 갖고 모든 정수 계열 형식이 가능하다.</li>
<li><code>enum-list</code> - 열거형의 목록</li>
</ul>
<h1 id="설명">설명</h1>
<p>C++11 이전의 <code>enum</code>은 다음과 같은 3가지 문제점을 보여줍니다.</p>
<ol>
<li><code>enum</code>은 암시적으로 <code>int</code>로 형변환이 되어, 정수로써 작동되기 원하지 않을 때, 오류를 발생시킨다.</li>
<li>허용 범위(scope) 밖에서도 열거자 이름들을 사용할 수 있기 때문에 이름 충돌이 발생한다.</li>
<li>전진 선언(forward declaration)이 불가능하다.</li>
</ol>
<p><code>enum class</code>는 이러한 문제점을 보완하기 위해 추가된 기능입니다. 전통적인 <code>enum</code>의 특징(값을 가진 이름들)과 클래스의 특징(범위를 가진 멤버, 암시적 변환의 부재)을 함쳐 기존의 문제를 해결한 키워드이며 다음과 같은 특징을 지닙니다.</p>
<ul>
<li><code>enum class</code>를 <code>int</code>로 형변환하고 싶다면 <u><strong>명시적</strong></u>으로 형변환을 해야 한다.</li>
<li>범위가 존재하며, 범위 밖의 열거자들의 이름 중복이 가능하다.</li>
<li>전진 선언이 가능하다.</li>
</ul>
<pre><code class="language-cpp">// classic enum
// 이름 충돌 발생
enum AlertWindows { Green, Yellow, Election, Red };
//enum AlertLinux {  Green, Yellow, Election, Red };

// C++11 enum
enum class Color { Red, Blue, Green };
enum class TrafficLight : short { Red, Yellow, Green };
enum class HexColorCode : int;

int main()
{
    AlertWindows alertWindow = 1;                // 오류: 모든 C++ 버전에서 불가능

    int data1 = Red;                            // 암시적으로 int로 형변환 가능
    int data2 = AlertWindows::Red;                // 오류: C++11 이전 버전에서는 불가능
    int data3 = Blue;                            // 오류: 사용할 수 있는 범위를 밖이기 때문에 사용할 수 없음
    int data4 = Color::Blue;                    // 오류: enum class는 암시적으로 형변환 할 수 없음
    int data5 = (int)Color::Blue;                // 명시적으로 int로 형변환 가능
    int redHexCode = (int)HexColorCode::Red;    // C++11 부터 전방 선언 가능
}

enum class HexColorCode : int { Red, Blue, Green };</code></pre>
<h1 id="참고-자료">참고 자료</h1>
<p><a href="https://en.cppreference.com/w/cpp/language/enum">cppreference</a><br><a href="https://learn.microsoft.com/ko-kr/cpp/cpp/enumerations-cpp?view=msvc-170">VisualC++ Docs</a><br><a href="https://blockdmask.tistory.com/405">BlockDMask</a><br><a href="https://boycoding.tistory.com/179">소년코딩</a>  </p>
]]></description>
        </item>
    </channel>
</rss>