<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>choi_612.log</title>
        <link>https://velog.io/</link>
        <description>soeun choi</description>
        <lastBuildDate>Tue, 07 Mar 2023 09:53:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. choi_612.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choi_612" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Network]OSI 7 layer]]></title>
            <link>https://velog.io/@choi_612/Network-layer</link>
            <guid>https://velog.io/@choi_612/Network-layer</guid>
            <pubDate>Tue, 07 Mar 2023 09:53:39 GMT</pubDate>
            <description><![CDATA[<h1 id="osi-7-계층">OSI 7 계층</h1>
<h2 id="네트워크-7계층">네트워크 7계층</h2>
<p>OSI 7 layer
Open Systems Interconnection Reference Model</p>
<p>컴퓨터 네트워크 프로토콜 디자인과 통신을 7계층으로 나눠 정리
즉 네트워크에서 통신이 일어나는 과정을 7단계로 정리 </p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/9066c729-6265-4073-99db-bd3118160181/image.png" alt="">
<img src="https://velog.velcdn.com/images/choi_612/post/062b02ff-7e70-4c7a-9853-d988e9c18880/image.png" alt=""></p>
<ol>
<li>OSI 7 Layer 
7 Application Layer
최상위 계층으로 사용자가 직접 이용할 수 있는 형태로 제공된다.
6 Presentation Layer 
데이터의 형태를 압축, 암호화, 복호화를 한다.
5 Session Layer –
하위계층과 상위계층과의 연결(데이터와 서비스).
4 Transport Layer 
데이터 분할(segment), TCP인 경우엔 오류확인&amp;전송확인이 불가할 때 재전송한다(오류제어).
3 Network Layer 
논리적 주소(IP Address)를 기반으로 경로를 설정한다(Routing).
2 Datalink Layer 
물리적 주소(MAC Address)를 기반으로 데이터 전송형태를 설정하며,
Tail이라는 꼬리로 데이터 이상유무를 확인한다. Encoding을 실시한다.
1 Physical Layer 
전기적 신호로 실제 전송</li>
</ol>
<p>2.데이터의 전송
데이터는 캡슐화 라는 절차를 거쳐 상대방에게 전송된다.
데이터는 각 계층을 지날 때마다 데이터의 맨 앞에 헤더가 붙게 되고,
추가된 헤더는 다음 계층에서 하나의 데이터로 취급받게 된다.
상위 계층에서 하위계층으로 캡슐화 되는 것을 encapsulation,
하위 계층에서 상위계층으로 역 캡슐화 De-encapsulation 이라고 한다.</p>
<p>3.De-capsulation과 encapsulation의 차이점
1.Datalink Layer
물리적 주소와 다른 데이터는 Drop, 자기 주소와 일치되는 데이터만 상위 계층으로 올린다.
수신된 데이터의 용량이 Tail용량과 다를 경우에 재전송을 요청한다.
 
2.Network Layer
논리적 주소와 다른 데이터는 Drop, 자기 주소와 일치하는 데이터만 상위로 올린다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] OS byte order]]></title>
            <link>https://velog.io/@choi_612/CS-OS-byte-order</link>
            <guid>https://velog.io/@choi_612/CS-OS-byte-order</guid>
            <pubDate>Tue, 10 Jan 2023 06:54:38 GMT</pubDate>
            <description><![CDATA[<p>{참고자료}
<a href="http://tcpschool.com/c/c_refer_endian">Endian</a>
<a href="https://fte.com/WebHelpII/NetDecoderAsync/Content/NetDecoderAsync/Configuration_Settings/I-O%20Configuration/Bit_Order.htm">bit order</a>
<a href="https://blog.naver.com/ansdbtls4067/220886567257">LSB/MSB</a>
<a href="http://www.tcpschool.com/c/c_operator_bitwise">비트연산자</a>
<a href="https://mygumi.tistory.com/361">비트 마스크</a>
<a href="https://coding-food-court.tistory.com/193">비트 마스킹</a></p>
<h2 id="byte--bit">byte / bit</h2>
<p>컴퓨터는 모든 데이터를 2진수로 처리 </p>
<h3 id="bit">bit</h3>
<p>data 최소 단위 
2진수값인 &#39;0&#39;, &#39;1&#39; 두 값 중 하나로 저장</p>
<h3 id="byte">byte</h3>
<p>문자 표현이 가능한 최소 단위
8bits = 1 byte
0x00 ~ 0xFF
0b00000000~0b11111111</p>
<h4 id="cc-진수-표기법">[C++/C] 진수 표기법</h4>
<p>10진수 (data) 
2진수는 0b(data)
8진수는 0(data)
16진수는 0x(data)</p>
<pre><code class="language-cpp">int a = 100;        // 10진수로 표현한 100
    int b = 0b01100100;    // 2진수로 표현한 100
    int c = 0144;    // 8진수로 표현한 100
    int d = 0x64;    // 16진수로 표현한 100</code></pre>
<h2 id="bit---msb--lsb">bit - MSB / LSB</h2>
<h4 id="msb">MSB</h4>
<p>Most Significant bit
최상위 비트</p>
<p>signed / unsigned data 따라 정보가 달라짐</p>
<h4 id="lsb">LSB</h4>
<p>Least Significant bit
최하위 비트 </p>
<p>데이터의 홀짝 / 난수발생 함수, 해시 함수, checksum 확인</p>
<h4 id="bit-1">bit</h4>
<p>2진수의 최상위 최하위 bit를 파악 
하나의 데이터에서 처음과 끝 및 방향 파악
MBS &lt; ---- LSB</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/3f808c1e-aaf4-4b05-a1ea-e36c0162c1c3/image.png" alt=""></p>
<p><strong>serial 통신시</strong>
송수신의 방향 유의
<img src="https://velog.velcdn.com/images/choi_612/post/e440b691-d05f-456c-9455-2129a771689b/image.png" alt=""></p>
<h2 id="byte-order">byte order</h2>
<p>컴퓨터 메모리에 바이트가 저장 되는 순서</p>
<h3 id="big-endian-빅엔디안">Big Endian 빅엔디안</h3>
<p>낮은 주소에 MSB 부터 저장
<img src="https://velog.velcdn.com/images/choi_612/post/f3f2214f-e331-4688-baf2-ee8e53c38db1/image.png" alt=""></p>
<p>32bit 0x12345678
byte로 저장시 0x12 0x34 0x56 0x78 로 저장</p>
<h3 id="little-endian-리틀엔디안">Little Endian 리틀엔디안</h3>
<p>낮은 주소에 LSB 부터 저장
<img src="https://velog.velcdn.com/images/choi_612/post/f6fc4556-358a-4a21-a499-b3343945169d/image.png" alt=""></p>
<p>32bit 0x12345678
byte로 저장시 0x78 0x56 0x34 0x12 로 저장</p>
<p>대부분의 인텔 CPU 계열에서 사용</p>
<hr>
<h4 id="내가-어려웠던-것">내가 어려웠던 것</h4>
<ol>
<li><p>byte order 저장 방식을 읽는 것 
메모리에는 Little Endian로  저장 
하지만 읽어올때는 사람이 읽는 방향과 같다
memcyp시 packet 값을 낮은 주소에 LSB로 저장
0x3DB 값의 4 bytes packet DB 03 00 00
data 가공시 유의 필요
&gt; 1byte씩 처리 또는 bit operation으로 가공</p>
</li>
<li><p>읽어 오는 packet 크기에 따라 data 값이 변경됨 
packet 값 32bits
00 00 AA AA</p>
<p>32bits로 읽어오면
Little endian 0xAAAA0000
Big endian 0x0000AAAA</p>
<p>하지만 16bit로 뒤에만 읽어오면
둘다 0xAAAA로 같다</p>
<p>packet 값 16 bits 
12 34</p>
<p>16bits로 읽어오면
Little endian 0x3412
Big endian 0x1234</p>
<p>Packet에 보이는 data 그대로 값이 아니라 OS rule에 따라 packet 값이 다를게 확인 될 수 있음
구현시 주의 할 것.</p>
</li>
</ol>
<hr>
<h2 id="bitwise-operator">bitwise operator</h2>
<p>비트 연산자 [C++/C 기반] </p>
<h3 id="기본-연산자">기본 연산자</h3>
<table>
<thead>
<tr>
<th>연산자</th>
<th>연산기능</th>
</tr>
</thead>
<tbody><tr>
<td>&amp;</td>
<td>비트 단위 AND연산</td>
</tr>
<tr>
<td>|</td>
<td>비트 단위 OR연산</td>
</tr>
<tr>
<td>^</td>
<td>비트 단위 XOR연산</td>
</tr>
<tr>
<td>~</td>
<td>단항 연산자 모든 피연사자 비트 반전</td>
</tr>
</tbody></table>
<h4 id="연산자">^연산자</h4>
<p>두 개의 비트가 서로 다른 경우에 1을 반환</p>
<table>
<thead>
<tr>
<th>연산</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>0 ^ 0</td>
<td>0</td>
</tr>
<tr>
<td>0 ^ 1</td>
<td>1</td>
</tr>
<tr>
<td>1 ^ 0</td>
<td>1</td>
</tr>
<tr>
<td>1 ^ 1</td>
<td>0</td>
</tr>
</tbody></table>
<h4 id="연산자-1">~연산자</h4>
<p> 0에서 1로, 1에서 0으로 반전시키는 NOT 연산/ 보수연산
 signed의 경우 MSB가 반전되어 부호가 바뀜 주의 </p>
<pre><code class="language-cpp"> int num1 = 15; //00000000 00000000 00000000 00001111
 int num2 = ~num1; // 11111111 11111111 11111111 11110000</code></pre>
<h3 id="shift">Shift</h3>
<h4 id="left-shift">left shift &lt;&lt;</h4>
<p>비트를 왼쪽으로 이동 남은 자리는 0으로 채움
 A * 2^B 연산과 동일 
A &lt;&lt; B
A의 비트 열을 B칸씩 왼쪽으로 이동
범위값을 넘어가면 값이 사라짐 주의</p>
<pre><code class="language-cpp">uint8_t num = 3; //0000 0011 
uint8_t shift num = num &lt;&lt; 4; // 0011 0000</code></pre>
<h4 id="right-shift">right shift &gt;&gt;</h4>
<p>비트를 오른쪽으로 이동 남은 자리는 0으로 채움
A / 2^B 연산과 동일
A &gt;&gt; B
A의 비트 열을 B칸씩 오른쪽으로 이동
범위값을 넘어가면 값이 사라짐 주의</p>
<pre><code class="language-cpp">uint8_t num = 7; //0000 0111 
uint8_t shift num = num &gt;&gt; 2; // 0000 0001</code></pre>
<p><strong>Shift 연산은 범위값에 아주 유의 하여야한다</strong></p>
<h3 id="bit-mask">bit mask</h3>
<p>비트를 바스킹 하는 기법</p>
<ol>
<li><p>모든비트 0 </p>
<pre><code class="language-cpp">int num = 0 //00000000 00000000 00000000 00000000</code></pre>
</li>
<li><p>모든비트 1 </p>
<pre><code class="language-cpp">int num = -1 //11111111 11111111 111111111</code></pre>
</li>
<li><p>특정 비트 삭제 
특정위치만 0 나머지 1 후 and 연산 해주기</p>
</li>
<li><p>특정 비트만 포함 시키기
특정 위치만 1 나머지 0 후 or 연산 해주기</p>
</li>
<li><p>특정 비트 포함 여부 확인 
해당 위치만 1 나머지 0 후 and 연산 해주기</p>
</li>
<li><p>특정 비트 토글 
해당 위치만 1하여 XOR 연산 수행하기 </p>
</li>
</ol>
<p><strong>토글</strong>
하나의 설정 값으로부터 다른 값으로 전환
1&gt;0, 0&gt;1
7. 마지막 비트 구하기
보수 체계 이용 
num과 -num의 and 연산 수행
8. 마지막 비트 삭제 하기
보수 체계 이용 
num과 num-1의 and 연산 수행</p>
<h2 id="data-type">data type</h2>
<p>자료형 [C++/C 기반] </p>
<h3 id="기본-자료형">기본 자료형</h3>
<p><img src="https://velog.velcdn.com/images/choi_612/post/254d47b0-da56-436f-93ab-2af7a6533d02/image.png" alt=""></p>
<p>크기가 표시된 정수 자료형을 사용하면 bit 계산시 유용하다 
data를 원하는 만큼만 잘라서 사용하기 용이 </p>
<pre><code class="language-c">int main( void ) 
{
   int8_t a;
   int16_t b;
   int32_t c;
   int64_t d;

   uint8_t e;
   uint16_t f;
   uint32_t g;
   uint64_t h; 
}</code></pre>
<p>Signed / unsigned 의 차이로 음수를 적용하는 지 파악
&gt; unsigned의 경우 2의 보수 이용이 안된다. </p>
<p>bool은 실제로 1bit지만 1byte만큼 차지 한다</p>
<p>data 값이 U를 붙이면 unsigned인 값이 된다</p>
<pre><code class="language-cpp">uint8_t a = 10U;

unsigned long a = 0UL;

0   // normal number is interpreted as int
0L  // ending with &#39;L&#39; makes it a long
0LL // ending with &#39;LL&#39; makes it long long
0UL // unsigned long

0.0  // decimal point makes it a double
0.0f // &#39;f&#39; makes it a float
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ Thread (2)]]></title>
            <link>https://velog.io/@choi_612/CPP14</link>
            <guid>https://velog.io/@choi_612/CPP14</guid>
            <pubDate>Mon, 11 Jul 2022 07:26:47 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>16장 C++ 쓰레드 766p-825p</p>
<hr>
<h1 id="컴퓨터-구조">컴퓨터 구조</h1>
<h2 id="메모리-memory">메모리 memory</h2>
<p>CPU &lt;-&gt; RAM(메모리)
물리적 거리로 인해 데이터를 읽어 오는데 시간이 걸림</p>
<h2 id="캐시-cache">캐시 Cache</h2>
<p>cpu 칩 안에 있는 작은 메모리 
RAM과는 다르게 연산을 하는 부분과 붙어있어 읽기와 쓰기 속도가 빠름
CPU가 특정한 주소에 있는 데이터에 접근 시, 일단 캐시에 있는지 확인, 있다면 해당 값을 읽고, 없다면 메모리 까지 갔다 오는 방식으로 진행</p>
<ul>
<li><p>Cache hit
캐시에 있는 데이터를 다시 요청해서 시간을 절약</p>
</li>
<li><p>Cache miss
캐시에 요청한 데이터가 없어서 메모리 까지 갔다 오는 것</p>
</li>
<li><p>CPU에서 캐시 작동 방식
메모리를 읽으면 일단 캐시에 저장
만일 캐시가 다 찼다면 특정한 방식에 따라 처리</p>
<ul>
<li>LRU(Least Recently Used)
  캐시를 날려버리고 그 자리에 새로운 캐시를 기록하는 방식
최근에 접근한 데이터를 자주 반복해서 접근한다면 매우 유리</li>
</ul>
</li>
</ul>
<h2 id="cpu-파이프라이닝pipelining">CPU 파이프라이닝(pipelining)</h2>
<ul>
<li>파이프라이닝(pipelining)
작업이 끝나기 전에, 다음 작업을 시작하는 방식으로 동시에 여러 개의 작업을 동시에 실행하는 것</li>
</ul>
<p>CPU 에서 명령어를 실행할 때 여러 단계를 거침</p>
<p>명령어를 읽어야 하고 (fetch), 
읽은 명령어가 무엇 인지 해석해야 하고 (decode), 
해석된 명령어를 실행하고(execute), 
마지막으로 결과를 써야함 (write).</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/b4e50557-3e23-4a4d-b688-9520878918dc/image.png" alt=""></p>
<p>컴파일러는 최대한 CPU 의 파이프라인을 효율적으로 활용할 수 있도록 명령어 재배치</p>
<h3 id="수정-순서modification-order">수정 순서(modification order)</h3>
<p>C++ 의 모든 객체들이 정의 가능
원자적 연산을 할 경우에 모든 쓰레드에서 같은 객체에 대해서 동일한 수정 순서 관찰 가능
순서 동일 -&gt; 수정 순서가 바뀌지 않는 것을 보장 </p>
<p><strong>따라서</strong>
명령어 재배치를 한다해도 
모든 쓰레드에서 변수의 수정 순서에 동의만 한다면 
문제될 것이 없음</p>
<h2 id="원자성atomicity">원자성(atomicity)</h2>
<p>원자처럼 쪼갤 수 없다</p>
<p>모든 연산들이 원자적일때
C++ 에서 모든 쓰레드들이 수정 순서에 동의해야만 함</p>
<h3 id="원자적-연산">원자적 연산</h3>
<p>CPU 가 명령어 1 개로 처리하는 명령
중간에 다른 쓰레드가 끼어들 여지가 전혀 없는 연산
원자적 연산들은 올바른 연산을 위해 굳이 뮤텍스가 필요하지 않음
-&gt; 연산 속도가 더 빠르다</p>
<p>c++에서 몇몇 타입들에 원자적인 연산을 쉽게 할 수 있도록 여러가지 도구들을 지원</p>
<h1 id="atomic">atomic</h1>
<p><code>std::atomic&lt;int&gt; counter(0);</code>
atomic 의 템플릿 인자로 원자적으로 만들고 싶은 타입을 전달
atomic 객체에서 제공하는 함수들을 통해서, 여러가지 원자
적인 연산들을 손쉽게 수행</p>
<p>example code </p>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void worker(std::atomic&lt;int&gt;&amp; counter) {
  for (int i = 0; i &lt; 10000; i++) {
    counter++;
  }
}

int main() {
  std::atomic&lt;int&gt; counter(0);

  std::vector&lt;std::thread&gt; workers;
  for (int i = 0; i &lt; 4; i++) {
    workers.push_back(std::thread(worker, ref(counter)));
  }

  for (int i = 0; i &lt; 4; i++) {
    workers[i].join();
  }

  std::cout &lt;&lt; &quot;Counter 최종 값 : &quot; &lt;&lt; counter &lt;&lt; std::endl;
</code></pre>
<blockquote>
<p>Counter 최종 값 : 40000</p>
</blockquote>
<p>CPU는 원래 한 명령어에서 메모리에 읽기 혹은 쓰기 둘 중 하나 밖에 못함 </p>
<p><strong>그러나</strong>
Assembly Language
<code>lock add DWORD PTR [rdi], 1</code> 
lock add 명령어를 통해 
rdi 에 위치한 메모리를 읽고 - 1 더하고 - 다시 rdi 에 위치한 메모리에 쓰기를 모두 함.</p>
<ul>
<li><p>해당 명령어를 컴파일러가 사용 가능 이유 
어느 CPU 에서 실행할 지(x8) 컴파일러가 알고 있기 때문에 이런 CPU 특이적인 명령어를 제공 가능함.
CPU에 따라 해당 명령이 없는 경우도 있음 </p>
</li>
<li><p>is_lock_free() 함수
lock free 에서의 lock 이 없다 의미 
뮤텍스와 같은 객체들의 lock, unlock 없이도 해당 연산을
올바르게 수행할 수 있다는 뜻</p>
<pre><code class="language-cpp">std::atomic&lt;int&gt; x;
std::cout &lt;&lt; &quot;is lock free ? : &quot; &lt;&lt; boolalpha &lt;&lt; x.is_lock_free() &lt;&lt; std::endl;</code></pre>
<blockquote>
<p>Is lock free ? : true</p>
</blockquote>
</li>
</ul>
<h2 id="memory_order">memory_order</h2>
<p>막기 위해서 메모리 재배치 순서를 강제를 위해 C++에서 제공
atomic 객체들의 경우 원자적 연산 시에 메모리에 접근할 때 어떠한 방식으로 접근하는지 지정 가능</p>
<h3 id="memory_order_relexed">memory_order_relexed</h3>
<p>메모리에서 읽거나 쓸 경우, 주위의 다른 메모리 접근들과 순서가 바뀌어도 무방
메모리 연산들사이에서 어떠한 제약조건도 없음
서로 다른 변수의 relaxed 메모리
단일 쓰레드 관점에서 결과가 동일하다면 연산은 CPU 마음대로 재배치 가능
-&gt; CPU에서 매우 빠른 속도로 실행 가능</p>
<p>relaxed 메모리 연산을 사용하면 예상치 못한 결과가 나올 수 있으니 주의 필요.</p>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;cstdio&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;
using std::memory_order_relaxed;

void t1(std::atomic&lt;int&gt;* a, std::atomic&lt;int&gt;* b) {
  b-&gt;store(1, memory_order_relaxed);      // b = 1 (쓰기)
  int x = a-&gt;load(memory_order_relaxed);  // x = a (읽기)

  printf(&quot;x : %d \n&quot;, x);
}

void t2(std::atomic&lt;int&gt;* a, std::atomic&lt;int&gt;* b) {
  a-&gt;store(1, memory_order_relaxed);      // a = 1 (쓰기)
  int y = b-&gt;load(memory_order_relaxed);  // y = b (읽기)

  printf(&quot;y : %d \n&quot;, y);
}

int main() {
  std::vector&lt;std::thread&gt; threads;

  std::atomic&lt;int&gt; a(0);
  std::atomic&lt;int&gt; b(0);

  threads.push_back(std::thread(t1, &amp;a, &amp;b));
  threads.push_back(std::thread(t2, &amp;a, &amp;b));

  for (int i = 0; i &lt; 2; i++) {
    threads[i].join();
  }
}</code></pre>
<blockquote>
<p>x : 1 
y : 0        </p>
</blockquote>
<p>or</p>
<blockquote>
<p>x : 0
y : 1</p>
</blockquote>
<p>or</p>
<blockquote>
<p>x : 1
y : 1</p>
</blockquote>
<ul>
<li>store &amp; load 
atomic 객체들에 대해서 원자적으로 쓰기와 읽기를 지원해주는 함수</li>
</ul>
<h3 id="memory_order_acquire-과-memory_order_release">memory_order_acquire 과 memory_order_release</h3>
<p>두 개의 다른 쓰레드들이 같은 변수의 release 와 acquire 를 통해서 동기화 (synchronize)를 수행 </p>
<h4 id="memory_order_release">memory_order_release</h4>
<p>해당 명령 이전의 모든 메모리 명령들이 해당 명령 이후로 재배치 되는 것을 금지</p>
<h4 id="memory_order_acquire">memory_order_acquire</h4>
<p>release 와는 반대로 
해당 명령 뒤에 오는 모든 메모리 명령들이 해당 명령 위로 재배치 되는 것을 금지</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;
using std::memory_order_relaxed;

std::atomic&lt;bool&gt; is_ready;
std::atomic&lt;int&gt; data[3];

void producer() {
  //store시에는 relaxed라서 재배치 가능하나
  data[0].store(1, memory_order_relaxed);
  data[1].store(2, memory_order_relaxed);
  data[2].store(3, memory_order_relaxed);

  // 아래 release 명령어를 넘어서 재배치는 안된다. 
  is_ready.store(true, std::memory_order_release);
}

void consumer() {
  // data 가 준비될 때 까지 기다린다.
  while (!is_ready.load(std::memory_order_acquire)) {
  }

  std::cout &lt;&lt; &quot;data[0] : &quot; &lt;&lt; data[0].load(memory_order_relaxed) &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;data[1] : &quot; &lt;&lt; data[1].load(memory_order_relaxed) &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;data[2] : &quot; &lt;&lt; data[2].load(memory_order_relaxed) &lt;&lt; std::endl;
}

int main() {
  std::vector&lt;std::thread&gt; threads;

  threads.push_back(std::thread(producer));
  threads.push_back(std::thread(consumer));

  for (int i = 0; i &lt; 2; i++) {
    threads[i].join();
  }
}</code></pre>
<blockquote>
<p>data[0] : 1
data[1] : 2
data[2] : 3</p>
</blockquote>
<h3 id="memory_order_acq_rel">memory_order_acq_rel</h3>
<p>acquire 와 release 를 모두 수행
읽기와 쓰기를 모두 수행하는 명령들, 
예를 들어서 fetch_add 와 같은 함수에서 사용</p>
<h3 id="memory_order_seq_cst">memory_order_seq_cst</h3>
<p>메모리 명령의 순차적 일관성(sequential consistency) 을 보장
순차적 일관성을 보장하기 위해서는 CPU 의 동기화 비용이 매우 큼
<strong>따라서</strong> 해당 명령은 정말 꼭 필요 할 때만 사용</p>
<ul>
<li>순차적 일관성
메모리 명령 재배치도 없고, 모든 쓰레드에서 모든 시점에 동일한 값을 관찰 가능 함 </li>
</ul>
<h3 id="atomic-의-메모리-memory_order-지정-연산-정리">atomic 의 메모리 memory_order 지정 연산 정리</h3>
<table>
<thead>
<tr>
<th>연산</th>
<th>허용된 memory order</th>
</tr>
</thead>
<tbody><tr>
<td>쓰기 (store)</td>
<td>memory_order_relaxed, memory_-order_release, memory_order_seq_-cst</td>
</tr>
<tr>
<td>읽기 (load)</td>
<td>memory_order_relaxed, memory_-order_consume, memory_order_-acquire, memory_order_seq_cst</td>
</tr>
<tr>
<td>읽고 - 수정하고 - 쓰기 (read - modify - write)</td>
<td>memory_order_relaxed, memory_-order_consume, memory_order_-acquire, memory_order_release, memory_order_acq_rel, memory_-order_seq_cst</td>
</tr>
</tbody></table>
<h1 id="동기-synchronous-와-비동기asynchronous-실행">동기 (synchronous) 와 비동기(asynchronous) 실행</h1>
<p>C++ 에서 제공하는 promise, future, packaged_task, async 를 잘 활용하면 
 귀찮게 mutex 나 condition_variable 을 사용하지 않고도 
 매우 편리하게 비동기적 작업을 수행</p>
<h2 id="동기적-synchronous실행">동기적 (synchronous)실행</h2>
<p>한 번에 하나씩 순차적으로 실행 되는 작업되도록 실행
동기적인 작업들은 한 작업이 끝날 때 까지 다음 작업으로 이동하지 않기 때문</p>
<h2 id="비동기적asynchronous-실행">비동기적(asynchronous) 실행</h2>
<p>프로그램의 실행이, 한 갈래가 아니라 여러 갈래로 갈라져서 동시에 진행 
어떠한 데이터를 다른 쓰레드를 통해 처리해서 받아내도록 실행
C++ 의 경우 위와 같이 명시적으로 쓰레드를 생성해서 적절히 수행하여 사용할 수 있도록 함 </p>
<h3 id="stdpromise-와-stdfuture">std::promise 와 std::future</h3>
<p>어떤 쓰레드 T 를 사용해서,
비동기적으로 값을 받아내겠다
미래에 (future)쓰레드 T 가 원하는 데이터를 돌려 주겠다 라는 약속 (promise)</p>
<p>생산자-소비자 패턴에서
promise 는 생산자 (producer) 의 역할을 수행하고,
future 는 소비자 (consumer) 의 역할을 수행</p>
<p>해당 패턴은 조건 변수로도 구현이 가능하나 더 복잡하다. 
또한 조건 변수와는 다르게 예외 전달도 가능.</p>
<ul>
<li><p>promise 객체 
연산을 수행 후에 돌려줄 객체의 타입을 템플릿 인자로 받음
연산이 끝난 다음에 promise 객체는 자신이 가지고 있는 future 객체에 값을 넣어줌.</p>
</li>
<li><p>future 객체
promise 가 future 에 값을 전달하기 전까지 wait으로 대기 
get 함수를 바로 호출하더라도 알아서 promise 가 future 에 객체를 전달할 때 까지 기다린 다음에 리턴<br>
future 에서 get 을 호출하면, 설정된 객체가 이동
따라서 <strong>절대로</strong> get 을 두 번 호출하면 안됩니다.</p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
using std::string;

void worker(std::promise&lt;string&gt;* p) {
  // 약속을 이행하는 모습. 해당 결과는 future 에 들어간다.
  p-&gt;set_value(&quot;some data&quot;);
}
int main() {
  std::promise&lt;string&gt; p;

  // 미래에 string 데이터를 돌려 주겠다는 약속.
  std::future&lt;string&gt; data = p.get_future();

  std::thread t(worker, &amp;p);

  // 미래에 약속된 데이터를 받을 때 까지 기다린다.
  data.wait();

  // wait 이 리턴했다는 뜻이 future 에 데이터가 준비되었다는 의미.
  // 참고로 wait 없이 그냥 get 해도 wait 한 것과 같다.
  std::cout &lt;&lt; &quot;받은 데이터 : &quot; &lt;&lt; data.get() &lt;&lt; std::endl;

  t.join();
}</code></pre>
<blockquote>
<p>받은 데이터 : some data</p>
</blockquote>
<h3 id="wait_for">wait_for</h3>
<p>정해진 시간 동안만 기다리고 그냥 진행 가능
promise 가 설정될 때 까지 기다리는 대신에 wait_for 에 전달된 시간 만큼 기다렸다가 바로 리턴
리턴하는 값은 현재 future 의 상태를 나타내는 future_status 객체 3가지</p>
<ul>
<li>future_status<ol>
<li>future_status::ready<ol start="2">
<li>future_status::timeout
wait_for 에 지정한 시간이 지났지만 값이 설정되지 않아서 리턴</li>
</ol>
</li>
<li>future_status::deferred
결과값을 계산하는 함수가 채 실행되지 않았다는 의미</li>
</ol>
</li>
</ul>
<h3 id="shared_future">shared_future</h3>
<p>여러 개의 다른 쓰레드에서 future를 get 할 필요성이 있을때 사용</p>
<p>condition_variable를 사용하서 구현 가능하나 future가 편리</p>
<p>shared_future 의 경우
future 와는 다르게 복사가 가능
복사본들이 모두 같은 객체를 공유
따라서 레퍼런스나 포인터로 전달할 필요가 없음</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;chrono&gt;
#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
using std::thread;

void runner(std::shared_future&lt;void&gt; start) {
  start.get();
  std::cout &lt;&lt; &quot;출발!&quot; &lt;&lt; std::endl;
}

int main() {
  std::promise&lt;void&gt; p;
  std::shared_future&lt;void&gt; start = p.get_future();

  thread t1(runner, start);
  thread t2(runner, start);
  thread t3(runner, start);
  thread t4(runner, start);

  // 참고로 cerr 는 std::cout 과는 다르게 버퍼를 사용하지 않기 때문에 터미널에
  // 바로 출력된다.
  std::cerr &lt;&lt; &quot;준비...&quot;;
  std::this_thread::sleep_for(std::chrono::seconds(1));
  std::cerr &lt;&lt; &quot;땅!&quot; &lt;&lt; std::endl;

  p.set_value();

  t1.join();
  t2.join();
  t3.join();
  t4.join();
}</code></pre>
<blockquote>
<p>준비...땅!
출발!
출발!
출발!
출발!</p>
</blockquote>
<h3 id="packaged_task">packaged_task</h3>
<p>C++에서 지원
비동기적 함수(정확히는 Callable - 즉 람다 함수, Functor 포함) 의 리턴값에 간단히 적용</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/5b1ab4a3-39ca-47e8-b130-4beab2ecddfd/image.png" alt=""></p>
<p>packaged_task 에 전달된 함수가 리턴할 때, 
그 리턴값을 promise 에 set_value 하고, 
만약에 예외를 던졌다면 promise 에 set_exception</p>
<p>해당 future 는 packaged_task 가 리턴하는 future 에서 접근 가능</p>
<p>packaged_task 
비동기적으로 수행할 함수 자체를 생성자의 인자로 받음
템플릿 인자로 해당 함수의 타입을 명시 필요</p>
<p>packaged_task 는 전달된 함수를 실행해서,
그 함수의 리턴값을 promise 에 설정</p>
<p>쓰레드에 굳이 promise 를 전달하지 않아도 알아서
packaged_task 가 함수의 리턴값을 처리해줘서 매우 편리</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;

int some_task(int x) { return 10 + x; }

int main() {
  // int(int) : int 를 리턴하고 인자로 int 를 받는 함수. (std::function 참조)
  std::packaged_task&lt;int(int)&gt; task(some_task);

  //해당 promise 에 대응되는 future 는 위와 같이 get_future 함수로 받음
  std::future&lt;int&gt; start = task.get_future();

  std::thread t(std::move(task), 5);

  std::cout &lt;&lt; &quot;결과값 : &quot; &lt;&lt; start.get() &lt;&lt; std::endl;
  t.join();
}</code></pre>
<blockquote>
<p>결과값 : 15</p>
</blockquote>
<h3 id="stdasync">std::async</h3>
<p>비동기적으로 실행을 하기 위해서는, 
쓰레드를 명시적으로 생성해서 실행하는 promise 나 packaged_task과 <strong>다르게</strong></p>
<p>std::async는
어떤 함수를 전달한다면, 아예 쓰레드를 알아서 만들어서 
해당 함수를 비동기적으로 실행하고, 그 결과값을 future 에 전달</p>
<ul>
<li><p>첫번째 인자 
std::launch::async : </p>
<pre><code>바로 쓰레드를 생성해서 인자로 전달된 함수를 실행</code></pre><p>std::launch::deferred : </p>
<pre><code>future 의 get 함수가 호출되었을 때 실행 (새로운 쓰레드를 생성하지 않음)</code></pre><p>  해당 함수를 굳이 바로 당장 비동기적으로 실행할 필요가 없다면 deferred 옵션 선택</p>
</li>
</ul>
<p>실행하는 함수의 결과값을 포함하는 future 를 리턴</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

// std::accumulate 와 동일
int sum(const std::vector&lt;int&gt;&amp; v, int start, int end) {
  int total = 0;
  for (int i = start; i &lt; end; ++i) {
    total += v[i];
  }
  return total;
}

int parallel_sum(const std::vector&lt;int&gt;&amp; v) {
  // lower_half_future 는 1 ~ 500 까지 비동기적으로 더함
  // 참고로 람다 함수를 사용하면 좀 더 깔끔하게 표현할 수 도 있다.
  // --&gt; std::async([&amp;v]() { return sum(v, 0, v.size() / 2); });
  std::future&lt;int&gt; lower_half_future =
    std::async(std::launch::async, sum, cref(v), 0, v.size() / 2);

  // upper_half 는 501 부터 1000 까지 더함
  int upper_half = sum(v, v.size() / 2, v.size());

  return lower_half_future.get() + upper_half;
}

int main() {
  std::vector&lt;int&gt; v;
  v.reserve(1000);

  for (int i = 0; i &lt; 1000; ++i) {
    v.push_back(i + 1);
  }

  std::cout &lt;&lt; &quot;1 부터 1000 까지의 합 : &quot; &lt;&lt; parallel_sum(v) &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>1 부터 1000 까지의 합 : 500500</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ Thread]]></title>
            <link>https://velog.io/@choi_612/CPP13</link>
            <guid>https://velog.io/@choi_612/CPP13</guid>
            <pubDate>Wed, 06 Jul 2022 05:44:52 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>16장 C++ 쓰레드 719p-825p</p>
<hr>
<h1 id="운영체제">운영체제</h1>
<ul>
<li><p>프로세스
  운영체제에서 실행되는 프로그램의 최소 단위
  CPU 코어에서 실행</p>
</li>
<li><p>스케줄러 scheduler
여러 프로세스를 돌리기 위해 컨텍스트 스위칭
어떤 프로그램을 실행시키고, 얼마 동안 실행 시키고, 스위치 시킬지 결정 </p>
</li>
</ul>
<h2 id="쓰레드">쓰레드</h2>
<p>CPU 코어에서 돌아가는 프로그램 단위
CPU 의 코어 하나에서는 한 번에 한 개의 쓰레드의 명령을 실행
한 개의 프로세스는 최소 한 개 쓰레드로 이루어져 있으며, 여러 개의 쓰레드로 구성도 가능</p>
<ul>
<li>쓰레드와 프로세스의 가장 큰 차이점
프로세스는 서로의 메모리를 접근할 수 없음</li>
<li><em>그러나*</em> 
같은 프로세스 내에 쓰레드 끼리는 메모리를 공유</li>
</ul>
<p><img src="https://velog.velcdn.com/images/choi_612/post/1e3b6803-6b60-4497-a7f2-2229b05645fe/image.png" alt=""></p>
<h2 id="멀티-쓰레드-multithread-프로그램">멀티 쓰레드 (multithread) 프로그램</h2>
<p>여러개의 쓰레드로 구성된 프로그램</p>
<ul>
<li><p>병렬화(parallelize)
어떠한 작업을 여러개의 다른 쓰레드를 이용해서 좀 더 빠르게 수행 가능
불가능 한 경우도 있다 ex) 피보나치 수열
연산들 간의 의존 관계가 많을 수로 병렬화 힘듦
다른 연산의 결과와 관계 없이 독립적으로 수행시 병렬화 쉬움</p>
<ul>
<li>A가 B에 의존(dependent)
어떠한 연산 (연산 A) 을 수행하기 위해
다른 연산 (연산 B)의 결과가 필요</li>
</ul>
</li>
<li><p>대기시간이 긴 작업
CPU 시간을 낭비하지 않고 효율적 작업 처리 가능</p>
</li>
</ul>
<h1 id="c--thread">C++  Thread</h1>
<p>example code</p>
<pre><code class="language-cpp">//헤더 파일 &lt;thread&gt;

#include &lt;iostream&gt;
#include &lt;thread&gt;
using std::thread;

void func1() {
  for (int i = 0; i &lt; 10; i++) {
      std::cout &lt;&lt; &quot;쓰레드 1 작동중! \n&quot;;
  }
}
void func2() {
  for (int i = 0; i &lt; 10; i++) {
      std::cout &lt;&lt; &quot;쓰레드 2 작동중! \n&quot;;
  }
}
void func3() {
  for (int i = 0; i &lt; 10; i++) {
      std::cout &lt;&lt; &quot;쓰레드 3 작동중! \n&quot;;
  }
}
int main() {
  //thread 객체 생성
  thread t1(func1);
  thread t2(func2);
  thread t3(func3);

  t1.join();
  t2.join();
  t3.join();
  //or t1.detach();
}</code></pre>
<blockquote>
<p>랜덤하게 쓰레드 # 작동중!이 출력됨 </p>
</blockquote>
<p>운영체제 스케줄링에 따라 다름 랜덤하게 각 쓰레드 실행</p>
<p>운영체제가 쓰레드들을 어떤코어에 할당하고, 
또 어떤 순서로 스케쥴 할지는 그 때 그 때 마다 상황에 맞게 바뀌기 때문에 그 결과를 정확히 예측할 수 없음.</p>
<ul>
<li><p>join()
해당하는 쓰레드들이 실행을 종료하면 리턴하는 함수
t1 이 종료하기 전 까지 리턴하지 않음.<br>
사용하지 않으면
쓰레드들의 내용이 실행되기전에 
main 함수가 종료되어서
쓰레드 객체들 (t1, t2, t3)의 소멸자가 호출됨<br>
join 되거나 detach 되지 않는 쓰레드들의 소멸자가 호출시엔 예외 발생</p>
</li>
<li><p>detach()
main은 종료되고
쓰레드는 알아서 백그라운드에서 돌아감.</p>
</li>
</ul>
<h2 id="쓰레드에-인자-전달">쓰레드에 인자 전달</h2>
<p>포인터의 형태로 전달
리턴 값이 없기 때문.</p>
<pre><code class="language-cpp">#include &lt;cstdio&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;
using std::thread;
using std::vector;

void worker(vector&lt;int&gt;::iterator start, vector&lt;int&gt;::iterator end, int* result) {
  int sum = 0;
  for (auto itr = start; itr &lt; end; ++itr) {
      sum += *itr;
  }
  *result = sum;

  // 쓰레드의 id 를 구한다.
  thread::id this_id = std::this_thread::get_id();
  printf(&quot;쓰레드 %x 에서 %d 부터 %d 까지 계산한 결과 : %d \n&quot;, this_id, *start,*(end - 1), sum);
}
int main() {
  vector&lt;int&gt; data(10000);
  for (int i = 0; i &lt; 10000; i++) {
      data[i] = i;
  }

  // 각 쓰레드에서 계산된 부분 합들을 저장하는 벡터
  vector&lt;int&gt; partial_sums(4);

  vector&lt;thread&gt; workers;
  for (int i = 0; i &lt; 4; i++) {
  workers.push_back(thread(worker, data.begin() + i * 2500, data.begin() + (i + 1) * 2500, &amp;partial_sums[i]));
  }

  for (int i = 0; i &lt; 4; i++) {
      workers[i].join();
  }

  int total = 0;
  for (int i = 0; i &lt; 4; i++) {
      total += partial_sums[i];
  }
  std::cout &lt;&lt; &quot;전체 합 : &quot; &lt;&lt; total &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>쓰레드 a754700 에서 0 부터 2499 까지 계산한 결과 : 3123750
쓰레드 9752700 에서 5000 부터 7499 까지 계산한 결과 : 15623750
쓰레드 9f53700 에서 2500 부터 4999 까지 계산한 결과 : 9373750
쓰레드 8f51700 에서 7500 부터 9999 까지 계산한 결과 : 21873750
전체 합 : 49995000</p>
</blockquote>
<h4 id="printf--cout">printf / cout</h4>
<ul>
<li>printf
&quot;...&quot; 안에 있는 문자열을 출력할 때, 컨텍스트 스위치가 되더라도 다른 쓰레드들이 그 사이에 메세지를 집어넣지 못하게 막음</li>
</ul>
<p><strong>하지만</strong></p>
<ul>
<li>cout
&lt;&lt; 사이 제외하고 전체 한줄이 출력되는 동안 중간에 다른 쓰레드가 출력하지 못하게 막는 것이 보장 안됨</li>
</ul>
<h2 id="쓰레드로-메모리-동시-접근-시-주의">쓰레드로 메모리 동시 접근 시 주의</h2>
<ul>
<li><p>경쟁 상태 (race condtion)
서로 다른 쓰레드에서 같은 메모리를 공유할 때 발생할 수 있는 문제</p>
</li>
<li><p>CPU 연산 처리
CPU 의 레지스터(register)에 데이터를 기록한 다음에 연산 수행<br>
모든 데이터들은 메모리에 저장되어 있고, 연산 할 때 할 때 마다 메모리에서 레지스터로 값을가져온 뒤에, 빠르게 연산을 하고, 다시 메모리에 가져다 놓는 식으로 작동</p>
</li>
</ul>
<p>스케줄러에 따라 다르게 쓰레드가 실행되므로 
멀티 쓰레드 프로그램의 경우 프로그램 실행 마다 그 결과가 달라질 수 있습니다. 
<strong>이게 무슨 말일까요?</strong> 
제대로 프로그램을 만들지 않았을 경우 디버깅이 <strong>겁나</strong> 어렵다는 뜻입니다.
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ</p>
<h2 id="뮤텍스-metex">뮤텍스 (metex)</h2>
<p>mutex
영어의 상호 배제 (mutual exclusion)</p>
<p>한번에 한 쓰레드만 해당 메모리 접근을 시키기 위해 사용 </p>
<p>한번에 한 쓰레드에서만 m 의 사용 권한 갖음
m.lock() 시 해당 뮤텍스 m 사용 권한을 갖는다
m.unlock() 시 m을 반환 
만약 사용시 m.lock을 하면 무한정 대기 </p>
<ul>
<li>임계 영역(critical section)
m.lock() 과 m.unlock() 사이에 한 쓰레드만이 유일하게 실행할 수 있는 코드 부분</li>
<li>lock_guard 
뮤텍스를 인자로 받아서 생성하게 되는데, 
이 때 생성자에서 뮤텍스를 lock 
그리고 lock_guard 가 소멸될 때 알아서 lock 했던 뮤텍스를 unlock</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;mutex&gt; // mutex 를 사용하기 위해 필요
#include &lt;thread&gt;
#include &lt;vector&gt;

void worker(int&amp; result, std::mutex&amp; m) {
  for (int i = 0; i &lt; 10000; i++) {
    m.lock();
    result += 1;
    m.unlock();
  }
}
int main() {
  int counter = 0;
  std::mutex m; // 우리의 mutex 객체
  std::vector&lt;std::thread&gt; workers;
  for (int i = 0; i &lt; 4; i++) {
    workers.push_back(std::thread(worker, std::ref(counter), std::ref(m)));
  }
  for (int i = 0; i &lt; 4; i++) {
      workers[i].join();
  }
  std::cout &lt;&lt; &quot;Counter 최종 값 : &quot; &lt;&lt; counter &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>Counter 최종 값 : 40000</p>
</blockquote>
<h2 id="데드락deadlock">데드락(deadlock)</h2>
<p>결국 아무 쓰레드도 연산을 진행하지 못하게 되는 상황
방지 하기 위해 취득한 뮤텍스는 사용이 끝나면 반드시 반환 할 것. 
소멸자에서 처리 가능(lock_guard 나 unique_lock 등을 이용)</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/254e87f1-0284-49b3-b0ba-2bb10c295274/image.png" alt=""></p>
<pre><code class="language-cpp">void worker1(std::mutex&amp; m1, std::mutex&amp; m2) {
  for (int i = 0; i &lt; 10000; i++) {
    std::lock_guard&lt;std::mutex&gt; lock1(m1);
    std::lock_guard&lt;std::mutex&gt; lock2(m2);
    // Do something
  }
}
void worker2(std::mutex&amp; m1, std::mutex&amp; m2) {
  for (int i = 0; i &lt; 10000; i++) {
    std::lock_guard&lt;std::mutex&gt; lock2(m2);
    std::lock_guard&lt;std::mutex&gt; lock1(m1);
    // Do something
  }
}</code></pre>
<p>worker1 에서 m2 를 lock 하기 위해서는 worker2 에서 m2 를 unlock 해야함. 
하지만 그러기 위해서는 worker2 에서 m1 을 lock 해야 함. 
그런데 이 역시 불가능.
왜냐하면 worker1 에 m1 을 lock 하고 있기 때문.
worker1 과 worker2 모두 이러지도 저러지도 못하는 데드락 상황.</p>
<p>해결 방법 </p>
<ul>
<li><p>우선권 
  한 쓰레드에게 우선권을 갖도록 할 수 있으나 기아 상태 발생 할 수 있음 <br></p>
<pre><code>  - 기아 상태(starvation)</code></pre><p>한 쓰레드가 다른 쓰레드에 비해 우위를 갖게 된다면, 한 쓰레드만 열심히 일하고 다른 쓰레드는 일할 수 없는 상태<br></p>
<pre><code>  - try_lock
  이 함수는 만일 m1 을 lock 할 수 있다면 lock 을 하고 true 를 리턴
  lock 을 할 수 없다면 기다리지 않고 그냥 false 를 리턴</code></pre></li>
<li><p>중첩된 Lock 을 사용하는 것을 피해라</p>
</li>
<li><p>Lock 을 소유하고 있을 때 유저 코드를 호출하는 것을 피해라</p>
</li>
<li><p>Lock 들을 언제나 정해진 순서로 획득해라</p>
</li>
</ul>
<h2 id="생산자producer-와-소비자consumer-패턴">생산자(Producer) 와 소비자(Consumer) 패턴</h2>
<ul>
<li><p>생산자
무언가 처리할 일을 받아오는 쓰레드를 의미</p>
</li>
<li><p>소비자
받은 일을 처리하는 쓰레드를 의미</p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;chrono&gt;  // std::chrono::miliseconds
#include &lt;iostream&gt;
#include &lt;mutex&gt;
#include &lt;queue&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void producer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
              int index) {
  for (int i = 0; i &lt; 5; i++) {
    // 웹사이트를 다운로드 하는데 걸리는 시간이라 생각하면 된다.
    // 각 쓰레드 별로 다운로드 하는데 걸리는 시간이 다르다.
    std::this_thread::sleep_for(std::chrono::milliseconds(100 * index));
    std::string content = &quot;웹사이트 : &quot; + std::to_string(i) + &quot; from thread(&quot; +
                          std::to_string(index) + &quot;)\n&quot;;

    // data 는 쓰레드 사이에서 공유되므로 critical section 에 넣어야 한다.
    m-&gt;lock();
    downloaded_pages-&gt;push(content);
    m-&gt;unlock();
  }
}

void consumer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
              int* num_processed) {
  // 전체 처리하는 페이지 개수가 5 * 5 = 25 개.
  while (*num_processed &lt; 25) {
    m-&gt;lock();
    // 만일 현재 다운로드한 페이지가 없다면 다시 대기.
    if (downloaded_pages-&gt;empty()) {
      m-&gt;unlock();  // (Quiz) 여기서 unlock 을 안한다면 어떻게 될까요?

      // 10 밀리초 뒤에 다시 확인한다.
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      continue;
    }

    // 맨 앞의 페이지를 읽고 대기 목록에서 제거한다.
    std::string content = downloaded_pages-&gt;front();
    downloaded_pages-&gt;pop();

    (*num_processed)++;
    m-&gt;unlock();

    // content 를 처리한다.
    std::cout &lt;&lt; content;
    std::this_thread::sleep_for(std::chrono::milliseconds(80));
  }
}

int main() {
  // 현재 다운로드한 페이지들 리스트로, 아직 처리되지 않은 것들이다.
  std::queue&lt;std::string&gt; downloaded_pages;
  std::mutex m;

  std::vector&lt;std::thread&gt; producers;
  for (int i = 0; i &lt; 5; i++) {
    producers.push_back(std::thread(producer, &amp;downloaded_pages, &amp;m, i + 1));
  }

  int num_processed = 0;
  std::vector&lt;std::thread&gt; consumers;
  for (int i = 0; i &lt; 3; i++) {
    consumers.push_back(
        std::thread(consumer, &amp;downloaded_pages, &amp;m, &amp;num_processed));
  }

  for (int i = 0; i &lt; 5; i++) {
    producers[i].join();
  }
  for (int i = 0; i &lt; 3; i++) {
    consumers[i].join();
  }
}</code></pre>
<ul>
<li><p>producer 쓰레드
웹사이트에서 페이지를 계속 다운로드 하는 역할
다운로드한 페이지들을 downloaded_pages 라는 큐에 저장</p>
</li>
<li><blockquote>
<p>FIFO 특성을 이용하기 위해서 </p>
</blockquote>
</li>
<li><p>consumer 쓰레드 
downloaded_pages 가 비어있지 않을 때 까지 계속 while 루프
sleep 시켜서 10 밀리초 뒤에 다시 확인 하도록 -&gt; 계속 확인하면 비효율content 를 처리
front 를 통해서 맨 앞의 원소를 얻은 뒤에,
pop 을 호출하면 맨 앞의 원소를 큐에서 제거
<img src="https://velog.velcdn.com/images/choi_612/post/7e926cac-f74f-41ed-9177-67a1b6802454/image.png" alt=""></p>
</li>
</ul>
<h2 id="condition_variable">condition_variable</h2>
<p>condition_variable wait 함수에 어떤 조건이 참이 될때 까지 기다릴지 해당 조건을 인자로 전달</p>
<p>-notify_one
모든 쓰레드를 깨워서 조건을 검사</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;chrono&gt;              // std::chrono::miliseconds
#include &lt;condition_variable&gt;  // std::condition_variable
#include &lt;iostream&gt;
#include &lt;mutex&gt;
#include &lt;queue&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void producer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
              int index, std::condition_variable* cv) {
  for (int i = 0; i &lt; 5; i++) {
    // 웹사이트를 다운로드 하는데 걸리는 시간이라 생각하면 된다.
    // 각 쓰레드 별로 다운로드 하는데 걸리는 시간이 다르다.
    std::this_thread::sleep_for(std::chrono::milliseconds(100 * index));
    std::string content = &quot;웹사이트 : &quot; + std::to_string(i) + &quot; from thread(&quot; +
                          std::to_string(index) + &quot;)\n&quot;;

    // data 는 쓰레드 사이에서 공유되므로 critical section 에 넣어야 한다.
    m-&gt;lock();
    downloaded_pages-&gt;push(content);
    m-&gt;unlock();

    // consumer 에게 content 가 준비되었음을 알린다.
    cv-&gt;notify_one();
  }
}

void consumer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
              int* num_processed, std::condition_variable* cv) {
  while (*num_processed &lt; 25) {
    std::unique_lock&lt;std::mutex&gt; lk(*m);

    cv-&gt;wait(
        lk, [&amp;] { return !downloaded_pages-&gt;empty() || *num_processed == 25; });

    if (*num_processed == 25) {
      lk.unlock();
      return;
    }

    // 맨 앞의 페이지를 읽고 대기 목록에서 제거한다.
    std::string content = downloaded_pages-&gt;front();
    downloaded_pages-&gt;pop();

    (*num_processed)++;
    lk.unlock();

    // content 를 처리한다.
    std::cout &lt;&lt; content;
    std::this_thread::sleep_for(std::chrono::milliseconds(80));
  }
}

int main() {
  // 현재 다운로드한 페이지들 리스트로, 아직 처리되지 않은 것들이다.
  std::queue&lt;std::string&gt; downloaded_pages;
  std::mutex m;
  std::condition_variable cv;

  std::vector&lt;std::thread&gt; producers;
  for (int i = 0; i &lt; 5; i++) {
    producers.push_back(
        std::thread(producer, &amp;downloaded_pages, &amp;m, i + 1, &amp;cv));
  }

  int num_processed = 0;
  std::vector&lt;std::thread&gt; consumers;
  for (int i = 0; i &lt; 3; i++) {
    consumers.push_back(
        std::thread(consumer, &amp;downloaded_pages, &amp;m, &amp;num_processed, &amp;cv));
  }

  for (int i = 0; i &lt; 5; i++) {
    producers[i].join();
  }

  // 나머지 자고 있는 쓰레드들을 모두 깨운다.
  cv.notify_all();

  for (int i = 0; i &lt; 3; i++) {
    consumers[i].join();
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수 객체]]></title>
            <link>https://velog.io/@choi_612/CPP12</link>
            <guid>https://velog.io/@choi_612/CPP12</guid>
            <pubDate>Fri, 24 Jun 2022 05:16:49 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>15장 함수 객체 705p-718p</p>
<hr>
<h1 id="callable">Callable</h1>
<p><code>( )</code>를 붙여서 호출(call) 할 수 있는 모든 것
함수, 객체, 람다 함수 etc.</p>
<h2 id="stdfunction">std::function</h2>
<p>함수 뿐만이 아니라 모든 Callable 들을 보관할 수 있는 객체
템플릿 인자로 전달 받을 함수의 타입(리턴값과 함수 인자들)을 갖음
<code>std::function&lt;리턴값(함수인자)&gt; [이름] = [받을 callable];</code></p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;

int some_func1(const std::string&amp; a) {
  std::cout &lt;&lt; &quot;Func1 호출! &quot; &lt;&lt; a &lt;&lt; std::endl;
  return 0;
}

struct S {
  void operator()(char c) { std::cout &lt;&lt; &quot;Func2 호출! &quot; &lt;&lt; c &lt;&lt; std::endl; }
};

int main() {
  std::function&lt;int(const std::string&amp;)&gt; f1 = some_func1;
  std::function&lt;void(char)&gt; f2 = S();
  std::function&lt;void()&gt; f3 = []() { std::cout &lt;&lt; &quot;Func3 호출! &quot; &lt;&lt; std::endl; };
  f1(&quot;hello&quot;);
  f2(&#39;c&#39;);
  f3();
}</code></pre>
<blockquote>
<p>Func1 호출! hello
Func2 호출! c
Func3 호출!</p>
</blockquote>
<h3 id="멤버-함수를-갖는-stdfunction">멤버 함수를 갖는 std::function</h3>
<p>멤버 함수</p>
<ul>
<li>구현 상 자신을 호출한 객체를 인자로 암묵적으로 받음</li>
<li>원래 인자에 추가적으로 객체를 받는 인자를 전달</li>
<li>암시적 변환이 발생하지 않으므로 &amp; 연산자를 통해 명시적으로 주소값을 전달</li>
<li>호출 하려는 객체를 인자로 전달하여 해당 객체의 멤버 함수를 호출한 것 처럼 사용</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;

class A {
  int c;

  public:
    A(int c) : c(c) {}
    int some_func() {
      std::cout &lt;&lt; &quot;비상수 함수: &quot; &lt;&lt; ++c &lt;&lt; std::endl;
      return c;
    }
    int some_const_function() const {
      std::cout &lt;&lt; &quot;상수 함수: &quot; &lt;&lt; c &lt;&lt; std::endl;
      return c;
    }
    static void st() {}
};

int main() {
  A a(5);
  std::function&lt;int(A&amp;)&gt; f1 = &amp;A::some_func;
  std::function&lt;int(const A&amp;)&gt; f2 = &amp;A::some_const_function;


  f1(a);
  f2(a);
}</code></pre>
<blockquote>
<p>비상수 함수: 6
상수 함수: 6</p>
</blockquote>
<h3 id="mem_fn">mem_fn</h3>
<p>std::mem_fn</p>
<p>전달된 멤버 함수를 function 객체로 만들어서 리턴
즉 멤버 함수들을 함수 객체로 만들어 전달 </p>
<ul>
<li>반복을 피하기 위해 사용 
인자로 전달하는 함수가 멤버 함수일때 
정확한 전달을 위해 std::function 으로 변환해서 전달 해야함 
해당 일을 반복하지 않기 위해 mem_fn 사용</li>
<li>&lt;functional&gt; 헤더를 추가</li>
<li>람다 함수로도 동일한 작업을 수행 가능</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;vector&gt;
using std::vector;

int main() {
  vector&lt;int&gt; a(1);
  vector&lt;int&gt; b(2);
  vector&lt;int&gt; c(3);
  vector&lt;int&gt; d(4);

  vector&lt;vector&lt;int&gt;&gt; container;
  container.push_back(a);
  container.push_back(b);
  container.push_back(c);
  container.push_back(d);

   vector&lt;int&gt; size_vec(4);
   //mem_fn을 통해 간단히 멤버 함수를 넘김
   transform(container.begin(), container.end(), size_vec.begin(),std::mem_fn(&amp;vector&lt;int&gt;::size));
   /*람다 함수로도 가능
   std::mem_fn(&amp;vector&lt;int&gt;::size)
   = [](const auto&amp;v){ return v.size()}
   */
   for (auto itr = size_vec.begin(); itr != size_vec.end(); ++itr) {
           std::cout &lt;&lt; &quot;벡터 크기 :: &quot; &lt;&lt; *itr &lt;&lt; std::endl;
  }
}</code></pre>
<blockquote>
<p>벡터 크기 :: 1
벡터 크기 :: 2
벡터 크기 :: 3
벡터 크기 :: 4</p>
</blockquote>
<h3 id="stdbind">std::bind</h3>
<p>원래 함수에 특정 인자를 붙여(bind) 줌
함수 객체 생성 시에 인자를 특정한 것으로 지정</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;

void add(int x, int y) {
  std::cout &lt;&lt; x &lt;&lt; &quot; + &quot; &lt;&lt; y &lt;&lt; &quot; = &quot; &lt;&lt; x + y &lt;&lt; std::endl;
}
void subtract(int x, int y) {
  std::cout &lt;&lt; x &lt;&lt; &quot; - &quot; &lt;&lt; y &lt;&lt; &quot; = &quot; &lt;&lt; x - y &lt;&lt; std::endl;
}
int main() {
  //add 함수의 첫번째 인자로 2를 bind
  auto add_with_2 = std::bind(add, 2, std::placeholders::_1);
  //두번째 인자로 새롭게 만들어진 함수 객체의 첫번째 인자 전달
  add_with_2(3);

  // 여러 인자 전달시 필요한 인자 이후는 다 무시된다.
  add_with_2(3, 4);

  //placeholders_1, _2 -&gt; 일일히 정의된 객체들
  auto subtract_from_2 = std::bind(subtract, std::placeholders::_1, 2);
  auto negate = std::bind(subtract, std::placeholders::_2, std::placeholders::_1);

  subtract_from_2(3); // 3 - 2 를 계산한다.
  negate(4, 2); // 2 - 4 를 계산한다
}</code></pre>
<blockquote>
<p>2 + 3 = 5
2 + 3 = 5
3 - 2 = 1
2 - 4 = -2</p>
</blockquote>
<p>※ 레퍼런스를 인자로 받는 함수 <strong>유의</strong>
bind 함수로 인자가 복사 되서 전달
따라서 명시적으로 레퍼런스 전달이 필요</p>
<pre><code class="language-cpp">// 명시적 레퍼런스 전달 std::ref() 사용
auto do_something_with_s1 = std::bind(do_something, std::ref(s1), std::placeholders::_1);</code></pre>
<ul>
<li>ref() 함수
  전달 받은 인자를 복사 가능한 레퍼런스로 변환 </li>
<li>cref() 함수
  ref 함수에서 const 레퍼런스 인자를 위해 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스마트 포인터]]></title>
            <link>https://velog.io/@choi_612/CPP11</link>
            <guid>https://velog.io/@choi_612/CPP11</guid>
            <pubDate>Fri, 24 Jun 2022 05:15:49 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>14장 스마트 포인터 671p-704p</p>
<hr>
<h3 id="c-자원-관리">C++ 자원 관리</h3>
<p>자원 해제 프로그래머가 직접 해제 필요 </p>
<p>한번 받은 자원은 직접 해제해주지 않는 이상
프로그램이 종료되기 전 까지 영원히 남아있음
종료시에 운영체제가 알아서 해제</p>
<p><strong>제대로 종료하지 않으면</strong> 
포인터는 존재하지 않는데 
heap에는 해당 객체가 남아있는 메모리 누수 발생 </p>
<p>** C++ 잘못된 방식 메모리 관리 할시의 문제 ** </p>
<p>1) 메모리를 사용한 후에 해제하지 않은 경우</p>
<ul>
<li>시스템 메모리가 부족해져서 서버가 죽어버리는 상황이 발생</li>
<li>RAII를 통해 사용이 끝난 메모리는 항상 해제시켜 메모리 누수 문제 사전 방지 가능</li>
</ul>
<p>2) 이미 해제된 메모리를 다시 참조하는 경우</p>
<ul>
<li>double free 버그
  이미 소멸된 객체를 다시 소멸시켜서 발생하는 버그</li>
<li>주로 만들어진 객체의 소유권이 명확하지 않아서 발생</li>
</ul>
<h1 id="스마트-포인터-smart-pointer">스마트 포인터 (smart pointer)</h1>
<p>RAII (Resource Acquisition Is Initialization)패턴</p>
<p>자원 (이 경우 메모리) 관리를 스택의 객체 (포인터 객체) 를 통해 수행</p>
<p>C++에서 두 가지 형태의 새로운 스마트 포인터를 제공</p>
<ul>
<li>unique_ptr</li>
<li>shared_ptr</li>
</ul>
<h2 id="unique_ptr">unique_ptr</h2>
<p>특정 객체에 유일한 소유권을 부여하는 포인터 객체</p>
<ul>
<li>-&gt; 연산자를 오버로드
  포인터를 다루는 것과 같이 사용 가능 </li>
<li>unique_ptr 은 어떤 객체의 유일한 소유권을 나타내는 포인터,         unique_ptr 가 소멸될 때, 가리키던 객체 역시 소멸</li>
<li>만약 다른 함수에서 unique_ptr 가 소유한 객체에 일시적으로 접근시,  get 을 통해 해당 객체의 포인터를 전달.</li>
<li>만약에 소유권을 이동을 원하면, unique_ptr 를 move</li>
</ul>
<p>example code </p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class A {
  int *data;
  public:
  A() {
    std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl;
    data = new int[100];
  }
  void some() { std::cout &lt;&lt; &quot;일반 포인터와 동일하게 사용가능!&quot; &lt;&lt; std::endl; }
  ~A() {
    std::cout &lt;&lt; &quot;자원을 해제함!&quot; &lt;&lt; std::endl;
    delete[] data;
  }
};

//unique_ptr 사용
void do_something() {
  //A* pa = new A();와 같다
  std::unique_ptr&lt;A&gt; pa(new A());
  pa-&gt;some();
}

int main() { do_something(); }</code></pre>
<blockquote>
<p>자원을 획득함!
일반 포인터와 동일하게 사용가능!
자원을 해제함!</p>
</blockquote>
<h3 id="unique_ptr-복사-불가능">unique_ptr 복사 불가능</h3>
<p> unique_ptr의 복사 생성자는 명시적으로 삭제되어 복사 불가능
 어떠한 객체를 유일하게 소유하여야 하기 때문
 <br>example code</p>
<pre><code class="language-cpp">class A {
  public:
  A(int a){};
  A(const A&amp; a) = delete; 
  /* 명시적으로 삭제하여 
  해당 함수를 사용할 수 없도록 함*/
};
int main() {
  A a(3); // 가능
  A b(a); // 불가능 (복사 생성자는 삭제됨)
}</code></pre>
<h3 id="unique_ptr-소유권-이전">unique_ptr 소유권 이전</h3>
<p>unique_ptr 복사는 불가능하나 소유권 이전은 가능
즉 이동생성자는 가능함 move로 소유권을 이동시키는 것이므로</p>
<p><code>std::unique_ptr&lt;A&gt; pb = std::move(pa);</code>
새로 생성된 pb이 소유권을 갖음
pa는 nullptr 즉 아무것도 가르키지 않음</p>
<ul>
<li>댕글링 포인터(dangling pointer)
  소유권이 이전된 unique_ptr을 재 참조 할 시 런타임 오류
  접근 하진 않도록 <strong>주의</strong></li>
</ul>
<h3 id="unique_ptr-함수-인자로-전달">unique_ptr 함수 인자로 전달</h3>
<p>레퍼런스로 넘기면 소유의 권한에 위배
<code>std::unique_ptr&lt;A&gt;&amp; ptr 형태</code> &lt; 올바르지 않음
원래의 포인터 주소값으로 넘김
<code>pa.get() 형태</code>로 넘기며<code>(A* ptr)형태</code>로 인자를 받으면 됨</p>
<h3 id="unique_ptr-쉽게-생성">unique_ptr 쉽게 생성</h3>
<p>std::make_unique 함수 사용
템플릿 인자로 전달된 클래스의 생성자에 인자들에 직접 완벽한 전달 수행</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class Foo {
  int a, b;
  public:
  Foo(int a, int b) : a(a), b(b) { std::cout &lt;&lt; &quot;생성자 호출!&quot; &lt;&lt; std::endl; }
  void print() { std::cout &lt;&lt; &quot;a : &quot; &lt;&lt; a &lt;&lt; &quot;, b : &quot; &lt;&lt; b &lt;&lt; std::endl; }
  ~Foo() { std::cout &lt;&lt; &quot;소멸자 호출!&quot; &lt;&lt; std::endl; }
};

int main() {
  auto ptr = std::make_unique&lt;Foo&gt;(3, 5);
  //std::unique_ptr&lt;Foo&gt; ptr(new Foo(3, 5)); 불필요
  ptr-&gt;print();
}</code></pre>
<blockquote>
<p>생성자 호출!
a : 3, b : 5
소멸자 호출!</p>
</blockquote>
<h3 id="unique_ptr-를-원소로-가지는-컨테이너">unique_ptr 를 원소로 가지는 컨테이너</h3>
<p>복사 생성자가 없는 특성을 지님</p>
<ul>
<li><p>vector push_back함수
  전달된 인자를 복사하여 집어 넣으므로 사용 불가능
  명시적으로 vector안으로 이동 &gt; push_back의 우측값 레퍼런스 오버로딩하여 사용</p>
<ul>
<li><p>emplace_back 함수
vector 안에 unique_ptr 을 직접 생성하며 집어넣기 가능
완벽한 전달(perfect forwarding) 을 통해,
직접 unique_ptr&lt;A&gt; 의 생성자에 전달
vector 맨 뒤에 unique_ptr&lt;A&gt; 객체를 생성
즉, 불필요한 이동 과정을 생략 가능
<br><strong>주의</strong>
어떤 생성자로 emplace_back하는지 확인 할 것.
요상한게 생성 될 수도 있다. 
int 1000인 원소를 원했으나 원소 1000개인 벡터를 추가하게 될 수 도...</p>
<p><br>example code</p>
<pre><code class="language-cpp">int main() {
  std::vector&lt;std::unique_ptr&lt;A&gt;&gt; vec;

  std::unique_ptr&lt;A&gt; pa(new A(1));
  //다음과 동일
  //vec.emplace_back(new A(1));
  vec.push_back(std::move(pa)); // 잘 실행됨
}</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="shared_ptr">shared_ptr</h2>
<p>여러 개의 스마트 포인터가 하나의 객체를 같이 소유 해야 하는 경우</p>
<p>특정 자원을 몇 개의 객체에서 가리키는지를 추적한 다음에, 그 수가 0 이 되야만 비로소 해제를 시켜주는 방식의 포인터</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/005a2e77-02df-437f-a0ee-ac124a2dcc87/image.png" alt=""></p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;
class A {
  int *data;
  public:
    A() {
      data = new int[100];
      std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl;
    }
    ~A() {
      std::cout &lt;&lt; &quot;소멸자 호출!&quot; &lt;&lt; std::endl;
      delete[] data;
    }
};

int main() {
  std::vector&lt;std::shared_ptr&lt;A&gt;&gt; vec;

  /* 여러개의 ptr로 하나의 객체를 가르키는 것 가능
  아래 3개는 모두 같은 객체를 가르키는 shared_ptr*/

  vec.push_back(std::shared_ptr&lt;A&gt;(new A()));
  vec.push_back(std::shared_ptr&lt;A&gt;(vec[0]));
  vec.push_back(std::shared_ptr&lt;A&gt;(vec[1]));

  // 벡터의 첫번째 원소를 소멸 시킨다.
  std::cout &lt;&lt; &quot;첫 번째 소멸!&quot; &lt;&lt; std::endl;
  vec.erase(vec.begin());
  // 그 다음 원소를 소멸 시킨다.
  std::cout &lt;&lt; &quot;다음 원소 소멸!&quot; &lt;&lt; std::endl;
  vec.erase(vec.begin());
  // 마지막 원소 소멸
  std::cout &lt;&lt; &quot;마지막 원소 소멸!&quot; &lt;&lt; std::endl;
  vec.erase(vec.begin());
  std::cout &lt;&lt; &quot;프로그램 종료!&quot; &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>자원을 획득함!
첫 번째 소멸!
다음 원소 소멸!
마지막 원소 소멸!
소멸자 호출!
프로그램 종료!</p>
</blockquote>
<p>A객체의 erase가 ref count가 0이 되어야만 소멸자를 호출
즉 마지막 erase만 소멸자를 호출 가능</p>
<h3 id="참조-개수-reference-count">참조 개수 (reference count)</h3>
<p>여러개의 소유권 가능 &gt; ref count 증가
해당 참조 개수가 0이 되어야 해당 객체 해제 가능
모든 스마트 포인터 들이 소멸되어야만 객체 파괴 가능해짐</p>
<ul>
<li>use_count 함수
ref count 개수 확인
<code>p1.use_count();</code></li>
<li>제어 블록 사용 하여 공유
실제 객체를 가리키는 shared_ptr 가 제어 블록(control block) 을 동적으로 할당
이후 shared_ptr 들이 이 제어 블록에 필요한 정보 공유하도록 구현
복사 생성할 때 마다 해당 제어 블록의 위치만 공유<br>
shared_ptr에서 
생성 시 제어 블록의 참조 개수 ++
소멸 시 제어 블록의 참조 개수 --하도록 작동
<img src="https://velog.velcdn.com/images/choi_612/post/eea79bfc-e0f1-4fb0-94e9-d6cce51c0d4c/image.png" alt=""></li>
</ul>
<h3 id="make_shared-로-생성">make_shared 로 생성</h3>
<p><code>std::shared_ptr&lt;A&gt; p1(new A());</code>&gt; 바람직 하지 않음
해당 방법은 동적 할당을 2번 해야함</p>
<ul>
<li>make_shared 함수
아예 두 개 합친 크기로 한 번 할당 하는 것이 훨씬 빠름
생성자의 인자들을 받아 객체 A 와 shared_ptr 의 제어블록 까지 
한 번에 동적 할당 후 생성한 shared_ptr 을 리턴
<code>std::shared_ptr&lt;A&gt; p1 = std::make_shared&lt;A&gt;();</code></li>
</ul>
<h3 id="shared_ptr-생성-시-주의">shared_ptr 생성 시 주의</h3>
<p>shared_ptr 은 인자로 주소값이 전달이 되면 해당 ptr이 첫번째가 되어버림
즉 주소값을 인자로 넘기면 각 ptr 제어블록 각각 생성</p>
<pre><code class="language-cpp">A* a = new A();
std::shared_ptr&lt;A&gt; pa1(a);
std::shared_ptr&lt;A&gt; pa2(a);</code></pre>
<p><img src="https://velog.velcdn.com/images/choi_612/post/5afff9dd-4862-4f03-b270-0d421dce38eb/image.png" alt="">
p1이 ref count 0으로 해당 A 객체를 삭제 하면
p2는 이미 해제된 객체를 가키며, 소멸 시키려 하여 오류가 발생</p>
<h3 id="enable_shared_from_this">enable_shared_from_this</h3>
<p>this 를 사용해서 shared_ptr 생성을 원하는 클래스가 있다면, 
즉 객체 내부에서 자기 자신을 가리키는 shared_ptr 원하면</p>
<p>enable_shared_from_this 를 상속 받아 사용</p>
<ul>
<li>enable_shared_from_this 클래스<ul>
<li>shared_from_this  멤버 함수 
이미 정의되어 있는 제어 블록을 사용해서 shared_ptr 을 생성<br></li>
<li><em>주의*</em></li>
<li>shared_from_this는 제어 블록 확인만 함 생성은 안함
즉, 해당 객체의 shared_ptr가 반드시 먼저 정의 되어야 함 <pre><code class="language-cpp">//불가능
A* a = new A();
std::shared_ptr&lt;A&gt; pa1 = a-&gt;get_shared_ptr();</code></pre>
</li>
</ul>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

class A : public std::enable_shared_from_this&lt;A&gt; {
  int *data;
  public:
    A() {
      data = new int[100];
      std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl;
    }
    ~A() {
      std::cout &lt;&lt; &quot;소멸자 호출!&quot; &lt;&lt; std::endl;
      delete[] data;
      }
    std::shared_ptr&lt;A&gt; get_shared_ptr() { return shared_from_this(); }
};
int main() {
  std::shared_ptr&lt;A&gt; pa1 = std::make_shared&lt;A&gt;();
  std::shared_ptr&lt;A&gt; pa2 = pa1-&gt;get_shared_ptr();

  std::cout &lt;&lt; pa1.use_count() &lt;&lt; std::endl;
  std::cout &lt;&lt; pa2.use_count() &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>자원을 획득함!
2
2
소멸자 호출!</p>
</blockquote>
<h3 id="서로-참조하는-shared_ptr">서로 참조하는 shared_ptr</h3>
<p>순환 참조
객체들을 더이상 사용하지 않아도 참조 개수가 절대로 0 이 될 수 없는 상황
shared_ptr 자체에 내재되어 있는 문제 &gt; weak_ptr로 해결
<img src="https://velog.velcdn.com/images/choi_612/post/587eb259-f559-47dd-9775-a482ff03e8a6/image.png" alt=""></p>
<h3 id="weak_ptr">weak_ptr</h3>
<p>일반 포인터와 shared_ptr 사이에 위치한 스마트 포인터</p>
<p>스마트 포인터 처럼 객체를 안전하게 참조
shared_ptr 와는 다르게 참조 개수 늘리지 않음.</p>
<p>어떤 객체를 weak_ptr가 가리키더라도, 
다른 shared_ptr를 사용하지 않으면 이미 메모리에서 소멸</p>
<p>weak_ptr 자체로는 원래 객체를 참조 불가능
반드시 shared_ptr 로 변환해서 사용
참조 하려는 객체가 이미 소멸되었다면 빈 shared_ptr 로 변환
아닐경우 해당 객체를 가리키는 shared_ptr 로 변환
-&gt; lock 함수를 통해 수행</p>
<ul>
<li>제어 블록을 메모리에서 해제
가리키는 weak_ptr 또한 0 개여야 가능
따라서 제어 블록에는 참조 개수와 더불어 약한 참조 개수 (weak count) 기록</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

class A {
  std::string s;
  std::weak_ptr&lt;A&gt; other;

  public:
    A(const std::string&amp; s) : s(s) { std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl; }
    ~A() { std::cout &lt;&lt; &quot;소멸자 호출!&quot; &lt;&lt; std::endl; }
    //weak_ptr 사용
    void set_other(std::weak_ptr&lt;A&gt; o) { other = o; }
    void access_other() {
      std::shared_ptr&lt;A&gt; o = other.lock();
      if (o) {
          std::cout &lt;&lt; &quot;접근 : &quot; &lt;&lt; o-&gt;name() &lt;&lt; std::endl;
      } else {
          std::cout &lt;&lt; &quot;이미 소멸됨 ㅠ&quot; &lt;&lt; std::endl;
      }
    }
    std::string name() { return s; }
};
int main() {
  std::vector&lt;std::shared_ptr&lt;A&gt;&gt; vec;
  vec.push_back(std::make_shared&lt;A&gt;(&quot;자원 1&quot;));
  vec.push_back(std::make_shared&lt;A&gt;(&quot;자원 2&quot;));

  vec[0]-&gt;set_other(vec[1]);
  vec[1]-&gt;set_other(vec[0]);

  // pa 와 pb 의 ref count 는 그대로다.
  std::cout &lt;&lt; &quot;vec[0] ref count : &quot; &lt;&lt; vec[0].use_count() &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;vec[1] ref count : &quot; &lt;&lt; vec[1].use_count() &lt;&lt; std::endl;

  // weak_ptr 로 해당 객체 접근하기
  vec[0]-&gt;access_other();

  // 벡터 마지막 원소 제거 (vec[1] 소멸)
  vec.pop_back();
  vec[0]-&gt;access_other(); // 접근 실패!
}</code></pre>
<blockquote>
<p>자원을 획득함!
자원을 획득함!
vec[0] ref count : 1
vec[1] ref count : 1
접근 : 자원 2
소멸자 호출!
이미 소멸됨 ㅠ
소멸자 호출!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[이동 연산]]></title>
            <link>https://velog.io/@choi_612/CPP10</link>
            <guid>https://velog.io/@choi_612/CPP10</guid>
            <pubDate>Fri, 24 Jun 2022 05:13:47 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>13장 우측값과 이동 연산 649p-670p</p>
<hr>
<h3 id="if-lvalue를-이동-시키고-싶다면">if lvalue를 이동 시키고 싶다면?</h3>
<pre><code class="language-cpp">template &lt;typename T&gt;
void my_swap(T &amp;a, T &amp;b) {
T tmp(a);
a = b;
b = tmp;
}</code></pre>
<p>swap 함수를 이용하여 임시 객처 생성 후 복사 복사
<img src="https://velog.velcdn.com/images/choi_612/post/91dbb46e-1abc-4435-acbe-03ac10bdea73/image.png" alt=""></p>
<ul>
<li>즉, 불필요한 복사를 3번 진행 
굳이 복사할 필요 없이 
각 MyString 객체의 string_content 주소값만 서로 바꿔주면 해결 가능 </li>
<li><em>하지만*</em> a 가 좌측값이라 이상태에서는 이동 생성자 오버로딩 불가능 </li>
<li><blockquote>
<p><strong>move 함수</strong> 사용하여 해결</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/choi_612/post/8c7e2944-4670-4309-b057-dbafe49d8937/image.png" alt=""></p>
<h2 id="move-함수-move-semantics">Move 함수 (Move semantics)</h2>
<p>좌측값을 우측값으로 바꾸어주는 함수 
인자로 받은 객체를 우측값으로 타입 변환만 수행하여 리턴</p>
<ul>
<li>&lt;utility&gt; library 사용 </li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;utility&gt;

class A {
  public:
  A() { std::cout &lt;&lt; &quot;일반 생성자 호출!&quot; &lt;&lt; std::endl; }
  A(const A&amp; a) { std::cout &lt;&lt; &quot;복사 생성자 호출!&quot; &lt;&lt; std::endl; }
  A(A&amp;&amp; a) { std::cout &lt;&lt; &quot;이동 생성자 호출!&quot; &lt;&lt; std::endl; }
};
int main() {
  A a;
  std::cout &lt;&lt; &quot;---------&quot; &lt;&lt; std::endl;
  A b(a);
  std::cout &lt;&lt; &quot;---------&quot; &lt;&lt; std::endl;
  A c(std::move(a));
}
</code></pre>
<blockquote>
<p>일반 생성자 호출!
---------
복사 생성자 호출!
---------
이동 생성자 호출!</p>
</blockquote>
<p>이동 연산자(operater =)을 사용한 swap
이동 생성이므로 복사 생성 보다 훨씬 빠르게 수행</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void my_swap(T &amp;a, T &amp;b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}</code></pre>
<p>이동 자체는  <strong>move를 통해서가 아니라</strong> 
우측값을 받는 함수들이 오버로딩 되면서 수행되는 것 </p>
<h2 id="완벽한-전달-perfect-forwarding">완벽한 전달 (perfect forwarding)</h2>
<p>문맥에 따라 &amp; L-value Reference, &amp;&amp; R-value Reference 을 구분하지 못하는 경우 
std::forward를 통해 타입을 분명하게 해줌
우측값 레퍼런스 일 때 에만 마치 move 를 적용한 것 처럼 작동
<code>std::forward&lt;T&gt;(u)</code></p>
<ul>
<li>주의 
  좌측값 레퍼런스일 때 move 를 해버린다면
  좌측값에 오버로딩 되는 g 가 아닌 
  우측값에 오버로딩 되는 g 가 호출
  -&gt; 이경우 forward  사용
  <code>g(std::forward&lt;T&gt;(u));</code></li>
</ul>
<h3 id="구분을-못하는-경우">구분을 못하는 경우</h3>
<p>C++ 컴파일러가 템플릿 타입을 추론할 때,
템플릿 인자 T 가 레퍼런스가 아닌 일반적인 타입이라면 
const 를 무시
즉, wrapper에서 T가 전부 class A 로 추론함
-&gt; 세 경우 전부 다 좌측값 레퍼런스를 호출</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
template &lt;typename T&gt;
  void wrapper(T u) {
  g(u);
}
class A {};
void g(A&amp; a) { std::cout &lt;&lt; &quot;좌측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(const A&amp; a) { std::cout &lt;&lt; &quot;좌측값 상수 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(A&amp;&amp; a) { std::cout &lt;&lt; &quot;우측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }

int main() {
  A a;
  const A ca;
  std::cout &lt;&lt; &quot;원본 --------&quot; &lt;&lt; std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout &lt;&lt; &quot;Wrapper -----&quot; &lt;&lt; std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}</code></pre>
<blockquote>
<p>원본 --------
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출
Wrapper -----
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출</p>
</blockquote>
<h3 id="보편적-레퍼런스universal-reference">보편적 레퍼런스(Universal reference)</h3>
<p>T&amp;&amp;
템플릿 인자 T에 대해서, 우측값 레퍼런스로 받는 형태
또한 다른 타입의 레퍼런스와 다르게 우측값 뿐만 아니라 좌측값도 받는 것도 가능</p>
<p>-&gt; 레퍼런스 겹침 규칙 (reference collapsing rule) 에 따라 T 의 타입을 추론함</p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

/* T&amp;&amp; 보편적 레퍼런스 
    forward 함수 사용 */
template &lt;typename T&gt;
void wrapper(T&amp;&amp; u) {
    g(std::forward&lt;T&gt;(u));
}

class A {};
void g(A&amp; a) { std::cout &lt;&lt; &quot;좌측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(const A&amp; a) { std::cout &lt;&lt; &quot;좌측값 상수 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(A&amp;&amp; a) { std::cout &lt;&lt; &quot;우측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }

int main() {
  A a;
  const A ca;
  std::cout &lt;&lt; &quot;원본 --------&quot; &lt;&lt; std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout &lt;&lt; &quot;Wrapper -----&quot; &lt;&lt; std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}</code></pre>
<blockquote>
<p>원본 --------
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출
Wrapper -----
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출</p>
</blockquote>
<p>참고
<a href="https://ansohxxn.github.io/cpp/chapter19-7/">https://ansohxxn.github.io/cpp/chapter19-7/</a>
<a href="https://lakanto.tistory.com/46">https://lakanto.tistory.com/46</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Rvalue & Lvalue]]></title>
            <link>https://velog.io/@choi_612/CPP9</link>
            <guid>https://velog.io/@choi_612/CPP9</guid>
            <pubDate>Wed, 15 Jun 2022 07:32:59 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>13장 우측값과 이동 연산 630p-648p</p>
<hr>
<h2 id="복사-생략copy-elision">복사 생략(Copy Elision)</h2>
<p>컴파일러 자체에서 복사를 생략해 버리는 작업
필요에 따라 굳이 임시 객체를 한 번 만들고, 이를 복사 생성할 필요 없음
(함수의 인자가 아닌) 함수 내부에서 생성된 객체를 그대로 리턴할 때, 복사 생략 수행</p>
<ul>
<li>nullptr
  기존의 NULL 대체
  포인터 주소값 0 정확히 명시 
  <code>string_content = nullptr;</code></li>
</ul>
<ul>
<li>만약 복사 생략 수행하지 않을시 
  쓸데 없는 복사를 두 번 하는데 상당한 자원이 소모
  어차피 똑같이 복사해서 생성할 것이면, 이미 생성된 (str1 + str2) 가 리턴한 객체를 str3으로 사용가능
  <img src="https://velog.velcdn.com/images/choi_612/post/3464439b-8c2a-4ae5-ac75-2207d77cd255/image.png" alt="">
  해당 문제를 해결하려면 
  str3 생성 시에 임시로 생성된 객체의 string_content 가리키는 문자열의 주소값을 str3 의 string_content
  문자열 사라짐 방지를 위해 nullptr로 사용
  <img src="https://velog.velcdn.com/images/choi_612/post/ee41d6e0-6e26-4d36-89de-a1c3f206167d/image.png" alt=""></li>
</ul>
<h2 id="좌측값-lvalue-와-우측값-rvalue">좌측값 (lvalue) 와 우측값 (rvalue)</h2>
<ul>
<li><p>좌측값 (lvalue)
  좌측값은 어떠한 표현식의 왼쪽 오른쪽 모두 가능
  메모리 상에서 존재하는 변수 </p>
</li>
<li><p>우측값(rvalue)
  표현식을 연산 시에만 잠깐 존재할 뿐 연산 후 사라지는 값</p>
<pre><code> 실체가 없는 값</code></pre><p>  우측값은 항상 오른쪽</p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">int a; // a 는 좌측값
int&amp; l_a = a; // l_a 는 좌측값 레퍼런스
int&amp; r_b = 3; // 3 은 우측값. 따라서 오류</code></pre>
<h3 id="좌측값-레퍼런스-lvalue-reference">좌측값 레퍼런스 (lvalue reference)</h3>
<p>’좌측값’ 에만 레퍼런스를 가질수 있음<br>&amp; 하나를 이용해서 정의하는 레퍼런스</p>
<pre><code class="language-cpp">int&amp; func1(int&amp; a) { return a; }
int func2(int b) { return b; }
int main() {
  int a = 3;
  func1(a) = 4;
  std::cout &lt;&lt; &amp;func1(a) &lt;&lt; std::endl;

  int b = 2;
  a = func2(b); // 가능
  func2(b) = 5; // 오류 1
  std::cout &lt;&lt; &amp;func2(b) &lt;&lt; std::endl; // 오류 
}</code></pre>
<p>위 예시 처럼 
래퍼런스 리턴시 리턴값의 주소값을 가져오는 것 가능
lvalue로 사용 가능
<strong>하지만</strong> 
일반 int값 리턴시 리턴 값은 실체가 없음</p>
<ul>
<li>const 레퍼런스 
예외적 타입에 한해서 우측값도 래퍼런스로 받기 가능
임시로 존재하는 객체의 값을 참조만 가능 변경 불가능 하기 때문</li>
</ul>
<h3 id="우측값-레퍼런스">우측값 레퍼런스</h3>
<p>&amp; 를 두 개 사용해서 정의
우측값만 특이적으로 받기 위해 사용 
레퍼런스를 갖는 해당 임시 객체가 소멸되지 안도록 한다.</p>
<p>example code</p>
<pre><code class="language-cpp">int a;
int&amp; l_a = a;
int&amp; ll_a = 3; // 불가능
int&amp;&amp; r_b = 3;
int&amp;&amp; rr_b = a; // 불가능</code></pre>
<h4 id="이동-생성자">이동 생성자</h4>
<p>우측값 래퍼런스를 사용한 이동 생성자</p>
<pre><code class="language-cpp">MyString::MyString(MyString&amp;&amp; str) {
  std::cout &lt;&lt; &quot;이동 생성자 호출 !&quot; &lt;&lt; std::endl;
  string_length = str.string_length;
  string_content = str.string_content;
  memory_capacity = str.memory_capacity;
  // 임시 객체 소멸 시에 메모리를 해제하지
  // 못하게 한다.
  str.string_content = nullptr;
}</code></pre>
<p>위 생성자는 Mystring타입의 우측값 인자 받음.
기존의 복사 생성자의 경우 문자열 전체를 새로 복사,
이동 생성자의 경우 단순히 주소값 하나만 복사.</p>
<p>주소값을 복사하므로
임시 객체가 소멸 시 delete 안 되도록 해야함 
즉, 소멸자 또한 nullptr이 아닐 때만 delete</p>
<ul>
<li><p>vector 복사 생성자 이용시 
  복사 생성 과정에서 예외 발생시 새로 할당한 메모리 소멸 후 예외처리
  <img src="https://velog.velcdn.com/images/choi_612/post/6accbc8b-87a7-408f-957d-172444859ae7/image.png" alt=""></p>
</li>
<li><p><strong>하지만</strong> vector 이동 생성자 이용시 
이동 생성의 경우 기존 메모리를 원소에 이동시켰으므로 기존에 메모리에 남은 원소들이 없음 
즉, 섯불리 해제 불가능 
vector는 noexcept이 아닌 이상 이동 생성자를 사용하지 않음
<img src="https://velog.velcdn.com/images/choi_612/post/68778c93-52c0-4906-be9e-b101abe493ea/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 예외 처리]]></title>
            <link>https://velog.io/@choi_612/CPP8</link>
            <guid>https://velog.io/@choi_612/CPP8</guid>
            <pubDate>Wed, 15 Jun 2022 07:31:42 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>12장 c++에서의 예외 처리 613p-629p</p>
<hr>
<h1 id="예외exception">예외(exception)</h1>
<p>정상적인 상황에서 벗어난 모든 예외적인 상황</p>
<p>문법 상 틀린 것이 없는 코드라도 실제로 실행 시 오류발생 가능성 있음</p>
<ul>
<li>예외를 계속 체크하는 데 있어서 시스템 로드가 생김 
  성능이 떨어지면 잘 안씀... </li>
</ul>
<h2 id="throw">throw</h2>
<p>예외가 발생 사실을 명시적으로 나타냄</p>
<ul>
<li>예외로 전달하고 싶은 객체 throw 
  여러 종류 예외 라이브러리 사용</li>
<li>throw 한 위치에서 즉시 함수가 종료 후 예외 처리 부분까지 점프
  점프하며서 stack 에 생성되었던 객체들을 빠짐없이 소멸</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">const T&amp; at(size_t index) const {
  if (index &gt;= size) {
    // 예외를 발생시킨다!
    throw std::out_of_range(&quot;vector 의 index 가 범위를 초과하였습니다.&quot;);
  }
  return data[index];
}</code></pre>
<h2 id="try-와-catch">try 와 catch</h2>
<p>try
무언가 예외가 발생할만한 코드가 실행</p>
<ul>
<li>예외가 없다면 
catch 문은 무시</li>
<li>예외가 있다면 
stack 에 생성된 모든 객체들의 소멸자들이 호출되고, 가장 가까운 catch 문으로 점프</li>
</ul>
<p>catch
throw 된 예외를 받는 부분
정의된 예외의 꼴에 맞는 객체로 받음</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
class Vector {
public:
  Vector(size_t size) : size_(size) {
    data_ = new T[size_];
    for (int i = 0; i &lt; size_; i++) {
      data_[i] = 3;
    }
  }
  const T&amp; at(size_t index) const {
    if (index &gt;= size_) {
      throw std::out_of_range(&quot;vector 의 index 가 범위를 초과하였습니다.&quot;);
    }
    return data_[index];
  }~
  Vector() { delete[] data_; }
private:
  T* data_;
  size_t size_;
}; 

int main() {
  Vector&lt;int&gt; vec(3);

  int index, data = 0;
  std::cin &gt;&gt; index;

  try {
  data = vec.at(index);
  } catch (std::out_of_range&amp; e) {
  std::cout &lt;&lt; &quot;예외 발생 ! &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
  }
  // 예외가 발생하지 않았다면 3을 이 출력되고, 예외가 발생하였다면 원래 data 에
  // 들어가 있던 0 이 출력된다.
  std::cout &lt;&lt; &quot;읽은 데이터 : &quot; &lt;&lt; data &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p><img src="https://velog.velcdn.com/images/choi_612/post/7430687f-e0c6-45ee-b630-0dee37b959b4/image.png" alt=""></p>
</blockquote>
<h3 id="스택-풀기-stack-unwinding">스택 풀기 (stack unwinding)</h3>
<p>catch 로 점프 하면서 스택 상에서 정의된 객체들을 소멸
각 객체의 소멸자 호출하여 소멸</p>
<h3 id="여러-종류의-예외-받기">여러 종류의 예외 받기</h3>
<p>catch문
여러 종류의 예외들을 받기 가능
각기 다른 타입으로 throw 하면 작동하는 catch가 달라짐 </p>
<ul>
<li>기반 파생 클래스의 catch 주의
   Parent catch 를 Child catch 보다 뒤에 쓰자 <br>
  &gt; 기반 클래스 레퍼런스나 포인터에 파생 클래스 객체를 정의시 
  catch에서 기반 클래스를 catch를 타게됨 </li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">int func(int c) {
  if (c == 1) {
      throw 10;
  } else if (c == 2) {
      throw std::string(&quot;hi!&quot;);
  } else if (c == 3) {
      throw &#39;a&#39;;
  } else if (c == 4) {
      throw &quot;hello!&quot;;
  }
  return 0;
}

int main() {
  int c;
  std::cin &gt;&gt; c;

  try {
      func(c);
  } catch (char x) {
      std::cout &lt;&lt; &quot;Char : &quot; &lt;&lt; x &lt;&lt; std::endl;
  } catch (int x) {
      std::cout &lt;&lt; &quot;Int : &quot; &lt;&lt; x &lt;&lt; std::endl;
  } catch (std::string&amp; s) {
      std::cout &lt;&lt; &quot;String : &quot; &lt;&lt; s &lt;&lt; std::endl;
  } catch (const char* s) {
      std::cout &lt;&lt; &quot;String Literal : &quot; &lt;&lt; s &lt;&lt; std::endl;
  }
}</code></pre>
<blockquote>
<p><img src="https://velog.velcdn.com/images/choi_612/post/e8d1a1c7-26f1-4c3d-b87b-6963cad4c5d3/image.png" alt=""></p>
</blockquote>
<h3 id="모든-예외-받기">모든 예외 받기</h3>
<p>어떤 예외를 throw but, 받는 catch 가 없을 수 있음 </p>
<ul>
<li>catch(...)
  try 안에서 발생한 모든 예외들을 받음</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">
int func(int c) {
  if (c == 1) {
      throw 1;
  } else if (c == 2) {
      throw &quot;hi&quot;;
  } else if (c == 3) {
      throw std::runtime_error(&quot;error&quot;);
  }
  return 0;
}

int main() {
  int c;
  std::cin &gt;&gt; c;

  try {
      func(c);
  } catch (int e) {
      std::cout &lt;&lt; &quot;Catch int : &quot; &lt;&lt; e &lt;&lt; std::endl;
  } catch (...) {
      std::cout &lt;&lt; &quot;Default Catch!&quot; &lt;&lt; std::endl;
  }
}</code></pre>
<blockquote>
<p><img src="https://velog.velcdn.com/images/choi_612/post/d6be0809-74dc-4ab4-b507-6bb9a2ed180f/image.png" alt=""></p>
</blockquote>
<h3 id="noexcept">noexcept</h3>
<p>명시적으로 함수가 예외 발생 시키지 않음을 표현 </p>
<p><code>int foo() noexcept {}</code></p>
<p>물론 키워드가 있다 해서 반드시 예외를 던지지 않지는 않음
만약 예외를 던지게 예외가 처리되지 않고 비정상 종료됨</p>
<ul>
<li>C++ 11 에서 부터 소멸자들은 기본적으로 noexcept 
  절대로 소멸자에서 예외를 던질 수 없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[c++ 표준 라이브러리(stl)]]></title>
            <link>https://velog.io/@choi_612/CPP7</link>
            <guid>https://velog.io/@choi_612/CPP7</guid>
            <pubDate>Fri, 10 Jun 2022 01:47:59 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>11장 c++ 표준 라이브러리(stl) 494p-555p
11장-3, 11장-4 556p - 612p </p>
<hr>
<h1 id="대표적-c-stl">대표적 C++ STL</h1>
<ul>
<li>컨테이너
  임의 타입의 객체를 보관</li>
<li>반복자
  컨테이너에 보관된 원소에 접근</li>
<li>알고리즘
  반복자들을 가지고 일련의 작업을 수행</li>
</ul>
<h1 id="컨테이너">컨테이너</h1>
<h2 id="시퀀스-컨테이너-sequence-container">시퀀스 컨테이너 (sequence container)</h2>
<p>배열 처럼 객체들을 순차적으로 보관
<strong>vector, list, deque</strong></p>
<ul>
<li>vector 
  일반적인 상황에서는 그냥 벡터를 사용. 거의 만능</li>
<li>list 
  맨 끝이 아닌 중간에 원소들을 추가하거나 제거 많고
  원소들을 순차적으로만 접근시 사용</li>
<li>deque 
  맨 처음과 끝 모두에 원소들을 추가 많으면 사용</li>
</ul>
<h3 id="vector">Vector</h3>
<p>원소들이 메모리 상에서 실제로 순차적으로 저장
임의의 위치의 원소를 접근 매우 빠르게 수행</p>
<ul>
<li><p>복잡도(complexity) 
  O(1)
  임의의 위치의 원소를 접근
  맨 뒤에 새로운 원소를 추가하거나 제거</p>
<p>  amortized O(1)</p>
<pre><code> 미리 더 많은 공간 할당 &gt; 다쓰면 O(n)</code></pre></li>
<li><p>접근
[ ] , at()</p>
</li>
<li><p>위치 
begin() 첫번째 원소 
end() 마지막 원소 +1칸 
빈 벡터를 표현 가능 
begin() == end() </p>
</li>
<li><p>추가 및 제거 
맨 뒤에 원소를 추가하거나 제거
push_back()/ pop_back() </p>
</li>
<li><p>size() 
  벡터의 크기를 리턴하는 함수
  값의 타입은 size_type 멤버 타입</p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">
int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(10); // 맨 뒤에 10 추가
  vec.push_back(20); // 맨 뒤에 20 추가
  vec.push_back(30); // 맨 뒤에 30 추가
  vec.push_back(40); // 맨 뒤에 40 추가

  for (std::vector&lt;int&gt;::size_type i = 0; i &lt; vec.size(); i++) {
  std::cout &lt;&lt; &quot;vec 의 &quot; &lt;&lt; i + 1 &lt;&lt; &quot; 번째 원소 :: &quot; &lt;&lt; vec[i] &lt;&lt; std::endl;
  }
}
</code></pre>
<blockquote>
<p>vec 의 1 번째 원소 :: 10
vec 의 2 번째 원소 :: 20
vec 의 3 번째 원소 :: 30
vec 의 4 번째 원소 :: 40</p>
</blockquote>
<h4 id="반복자">반복자</h4>
<p>RandomAccessIterator
반복자 </p>
<p>컨테이너에 iterator 멤버 타입</p>
<ul>
<li>vetcor  begin() end()
std::vector&lt;&gt;::iterator 멤버 타입으로 정의
<img src="https://velog.velcdn.com/images/choi_612/post/d7a2e2e1-be29-4e31-8381-52add6751d3c/image.png" alt=""></li>
</ul>
<ul>
<li><p>* 연산자 오버로딩
  itr이 가르키는 원소 확인 </p>
</li>
<li><p>+ 연산자 오버로딩
  itr에서 해당 크기만큼 덜어진 원소 가르킴</p>
</li>
<li><p>insert erase 사용 가능 
  ※ 오류 주의 
  erase로 itr를 지우면 해당 값은 유효한 값이 아님     반드시 새로운 값을 넣어줘야 itr을 다시 사용 가능
  디버깅시 주의  </p>
</li>
<li><p>const_iterator
  const 포인터처럼 가리키고 있는 원소의 값을 바꿀 수 없음</p>
</li>
<li><p>역반복자 (reverse iterator)
 벡터 뒤에서 부터 앞으로 거꾸로 간다는 특징
 (const_reverse_iterator 타입도 있음)
 rbegin() / rend()
 <code>std::vector&lt;int&gt;::reverse_iterator r_iter = vec.rbegin();</code>
 <img src="https://velog.velcdn.com/images/choi_612/post/760a0dff-6f75-4a4a-84ce-b1431b551607/image.png" alt=""></p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

template &lt;typename T&gt;
void print_vector(std::vector&lt;T&gt;&amp; vec) {
  // 전체 벡터를 출력하기
  // typename 필요 &gt; iterator 가 std::vector&lt;T&gt;
의 의존 타입
  for (typename std::vector&lt;T&gt;::iterator itr = vec.begin(); itr != vec.end();
  ++itr) {
      std::cout &lt;&lt; *itr &lt;&lt; std::endl;
  }
}

int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(10);
  vec.push_back(20);
  vec.push_back(30);
  vec.push_back(40);

  std::cout &lt;&lt; &quot;처음 벡터 상태&quot; &lt;&lt; std::endl;
  print_vector(vec);
  std::cout &lt;&lt; &quot;----------------------------&quot; &lt;&lt; std::endl;

  // vec[2] 앞에 15 추가
  vec.insert(vec.begin() + 2, 15);
  print_vector(vec);
  std::cout &lt;&lt; &quot;----------------------------&quot; &lt;&lt; std::endl;

  // vec[3] 제거
  vec.erase(vec.begin() + 3);
  print_vector(vec);
}</code></pre>
<blockquote>
<p>처음 벡터 상태
10
20
30
40
----------------------------
10
20
15
30
40
----------------------------
10
20
15
40</p>
</blockquote>
<h4 id="범위-기반-for-문-range-based-for-loop">범위 기반 for 문 (range based for loop)</h4>
<p>for문 패턴을 매우 간단하게 나타낼 수 있는 방식을 제공</p>
<p><code>for (/* 원소를 받는 변수 정의 */ : /* 컨테이너 */) {
}</code></p>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void print_vector(std::vector&lt;T&gt;&amp; vec) {
  // 전체 벡터를 출력하기
  for (typename std::vector&lt;T&gt;::iterator itr = vec.begin(); itr != vec.end();
  ++itr) {
      std::cout &lt;&lt; *itr &lt;&lt; std::endl;
  }
}
template &lt;typename T&gt;
void print_vector_range_based(std::vector&lt;T&gt;&amp; vec) {
  // 전체 벡터를 출력하기
  // 범위 기반 for 문 레퍼런스 사용
  for (const auto&amp; elem : vec) {
      std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}
int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);
  vec.push_back(4);
  std::cout &lt;&lt; &quot;print_vector&quot; &lt;&lt; std::endl;
  print_vector(vec);
  std::cout &lt;&lt; &quot;print_vector_range_based&quot; &lt;&lt; std::endl;
  print_vector_range_based(vec);
  return 0;
}</code></pre>
<blockquote>
<p>print_vector
1
2
3
4
print_vector_range_based
1
2
3
4</p>
</blockquote>
<h3 id="list">List</h3>
<p>양방향 연결 구조를 가진 자료형</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/9805216f-c5f2-42f6-b631-56e9606f6dc5/image.png" alt=""></p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;list&gt;

int main() {
  std::list&lt;int&gt; lst;
  lst.push_back(10);
  lst.push_back(20);
  lst.push_back(30);
  lst.push_back(40);

  for (std::list&lt;int&gt;::iterator itr = lst.begin(); itr != lst.end(); ++itr) {
      std::cout &lt;&lt; *itr &lt;&lt; std::endl;
  }
}</code></pre>
<blockquote>
<p>10
20
30
40</p>
</blockquote>
<ul>
<li><p>접근 
임의의 위치의 원소에 바로 접근 불가능
임의의 위치의 원소 접근을 위해 하나씩 링크를 따라가야함 </p>
</li>
<li><p>반복자 (BidirectionalIterator)</p>
</li>
<li><p>+, -- 연산자는 가능 &lt; 한칸씩 움직인 위치 접근 가능 
+ 3, - 5 연산자 불가능 &lt; 임의의 위치 원소 접근 못함</p>
</li>
<li><p>앞뒤의 insert erase 사용 
원소를 추가하거나 제거하는 작업이 O(1) 으로 매우 빠르게 수행됨
원하는 위치 앞과 뒤에 있는 링크 값만 바꿈<br>
원소를 지워도 반복자가 무효화 안음 
itr를 계속 사용가능  </p>
</li>
</ul>
<h3 id="deque---double-ended-queue">deque - double ended queue</h3>
<p>덱</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/546b74d0-6a0a-4d9e-bdcc-d10c5410e8b7/image.png" alt=""></p>
<p>실행 속도를 위해 메모리를 (많이) 희생하는 컨테이너</p>
<ul>
<li>O(1)으로 수행
  임의의 위치의 원소에 접근
  맨 앞 및 맨 뒤에 원소를 추가/제거 하는 작업</li>
<li>원소들이 실제로 메모리 상에서 연속적으로 존재 않음
  추가적 메모리 필요 &lt; 원소들의 저장 위치 정보 보관 필요</li>
<li>메모리<br>  일정 크기로 잘려서 각각의 블록 속에 존재
  블록의 메모리 위치 주소 정보 저장을 위해 벡터 사용
  새로 할당 시에 앞쪽 및 뒤쪽 모두에 공간 남겨 사용</li>
</ul>
<pre><code class="language-cpp">#include &lt;deque&gt;
#include &lt;iostream&gt;

template &lt;typename T&gt;
void print_deque(std::deque&lt;T&gt;&amp; dq) {
  // 전체 덱을 출력하기
  std::cout &lt;&lt; &quot;[ &quot;;
  for (const auto&amp; elem : dq) {
      std::cout &lt;&lt; elem &lt;&lt; &quot; &quot;;
  }
  std::cout &lt;&lt; &quot; ] &quot; &lt;&lt; std::endl;
}

int main() {
  std::deque&lt;int&gt; dq;
  dq.push_back(10);
  dq.push_back(20);
  dq.push_front(30);
  dq.push_front(40);

  std::cout &lt;&lt; &quot;초기 dq 상태&quot; &lt;&lt; std::endl;
  print_deque(dq);
  std::cout &lt;&lt; &quot;맨 앞의 원소 제거&quot; &lt;&lt; std::endl;
  dq.pop_front();
  print_deque(dq);
}</code></pre>
<blockquote>
<p>초기 dq 상태
[ 40 30 10 20 ]
맨 앞의 원소 제거
[ 30 10 20 ]</p>
</blockquote>
<h2 id="연관-컨테이너associative-container">연관 컨테이너(associative container)</h2>
<p> 키(key) - 값(value) 구조
 특정한 키를 넣으면 이에 대응되는 값 리턴
 키와 값 모두 임의의 타입의 객체 가능</p>
<p> 키의 존재 유무 / 대응 되는 값 질의 두가지 타입으로 나뉨
 set, mulitset / map, multimap</p>
<ul>
<li>set
  데이터의 존재 유무 만 궁금할 경우 </li>
<li>multiset
중복 데이터를 허락할 경우
insert, erase, find 모두 O(log N). 최악도 O(log N))</li>
<li>map
 데이터에 대응되는 데이터를 저장하고 싶은 경우 </li>
<li>multimap
중복 키를 허락할 경우
insert, erase, find 모두 O(log N). 최악도 O(log N))<ul>
<li>unordered_set, unordered_map
속도가 매우매우 중요해서 최적화를 해야하는 경우 
insert, erase, find 모두 O(1)
최악은 O(N) 
그러므로 해시함수와 상자 개수 설정 유의</li>
</ul>
</li>
</ul>
<h3 id="셋set">셋(set)</h3>
<p> 트리 구조로 되어 있음 find 최적화
 <img src="https://velog.velcdn.com/images/choi_612/post/c8262121-eef0-4ad7-8fb5-b4d3371b1577/image.png" alt=""></p>
<ul>
<li><p>추가 
 insert()
 위치정보 없이 추가
 추가 시 정렬된 상태를 유지 O(logN)</p>
</li>
<li><p>원소
   위치 정보가 아닌 유무가 중요한 정보 </p>
</li>
<li><p>반복자 BidirectionalIterator
   순차적 접근만 가능 </p>
</li>
<li><p>find 
   원소의 존재 유무 확인
  정렬된 상태 유지하므로 O(logN)
  존재 시 반복자 리턴
  존재 하지 않을시 s.end() 리턴- 중복 
   중복된 원소 추가 불가능 
  있는데 추가시 무시함 </p>
</li>
</ul>
<p>-원하는 타입 셋 사용
    크기 비교가 있어야 사용가능 &lt; 정렬을 사용하기 때문
    즉 operater &lt;에 대한 정의로 오버로딩 필요
    operator&lt; 조건이 모두 만족해야 접근 가능
    또는 함수 객체 사용
    const 사용 하여 전달
    : 셋 내부적으로 정렬 시에 상수 반복자를 사용</p>
<p> example code</p>
<pre><code class="language-cpp"> #include &lt;iostream&gt;
#include &lt;set&gt;
template &lt;typename T&gt;
void print_set(std::set&lt;T&gt;&amp; s) {
  // 셋의 모든 원소들을 출력하기
  std::cout &lt;&lt; &quot;[ &quot;;
  for (typename std::set&lt;T&gt;::iterator itr = s.begin(); itr != s.end(); ++itr) {
      std::cout &lt;&lt; *itr &lt;&lt; &quot; &quot;;
  }
  std::cout &lt;&lt; &quot; ] &quot; &lt;&lt; std::endl;
}

int main() {
  std::set&lt;int&gt; s;
  s.insert(10);
  s.insert(50);
  s.insert(20);
  s.insert(40);
  s.insert(30);

  std::cout &lt;&lt; &quot;순서대로 정렬되서 나온다&quot; &lt;&lt; std::endl;
  print_set(s);

  std::cout &lt;&lt; &quot;20 이 s 의 원소인가요? :: &quot;;
  auto itr = s.find(20);
  if (itr != s.end()) {
      std::cout &lt;&lt; &quot;Yes&quot; &lt;&lt; std::endl;
  } else {
      std::cout &lt;&lt; &quot;No&quot; &lt;&lt; std::endl;
  }

  std::cout &lt;&lt; &quot;25 가 s 의 원소인가요? :: &quot;;
  itr = s.find(25);
  if (itr != s.end()) {
      std::cout &lt;&lt; &quot;Yes&quot; &lt;&lt; std::endl;
  } else {
    std::cout &lt;&lt; &quot;No&quot; &lt;&lt; std::endl;
  }
}</code></pre>
<blockquote>
<p>순서대로 정렬되서 나온다
[ 10 20 30 40 50 ]
20 이 s 의 원소인가요? :: Yes
25 가 s 의 원소인가요? :: No</p>
</blockquote>
<h3 id="맵-map">맵 (map)</h3>
<p>키에 대응되는 값(value) 까지도 같이 보관</p>
<ul>
<li><p>키와 값
템플릿 인자 2 개
첫번째는 키의 타입, 두 번째는 값의 타입 <br>
맵에 원소를 넣기 위해서는 반드시 std::pair 객체를 전달
make_pair() 사용 &lt; pair 역시 stl (utility 헤더 사용)
operator[]도  사용가능 </p>
</li>
<li><p>반복자
맵 반복자가 std::pair 객체
tr-&gt;first 키,  itr-&gt;second 원소</p>
</li>
<li><p>탐색
키를 통해 원소 확인 
find함수 사용
<code>itr = m.find(key) itr-&gt;second</code>
find 없는키 참조시 end()리턴 
또는 
[]로 맵에 없는 키를 참조
자동으로 값의 디폴트 생성자를 호출해서 원소를 추가 <br>
중복된 원소 추가 불가능 
있는데 추가시 무시함 </p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;string&gt;
template &lt;typename K, typename V&gt;
void print_map(std::map&lt;K, V&gt;&amp; m) {
  // 맵의 모든 원소들을 출력하기
  for (auto itr = m.begin(); itr != m.end(); ++itr) {
      std::cout &lt;&lt; itr-&gt;first &lt;&lt; &quot; &quot; &lt;&lt; itr-&gt;second &lt;&lt; std::endl;
  }
}

int main() {
  std::map&lt;std::string, double&gt; pitcher_list;

  // 맵의 insert 함수는 pair 객체를 인자로 받습니다.
  pitcher_list.insert(std::pair&lt;std::string, double&gt;(&quot;박세웅&quot;, 2.23));
  pitcher_list.insert(std::pair&lt;std::string, double&gt;(&quot;해커 &quot;, 2.93));
  pitcher_list.insert(std::pair&lt;std::string, double&gt;(&quot;피어밴드 &quot;, 2.95));

  // 타입을 지정하지 않아도 간단히 std::make_pair 함수로
  // std::pair 객체를 만들 수 도 있습니다.
  pitcher_list.insert(std::make_pair(&quot;차우찬&quot;, 3.04));
  pitcher_list.insert(std::make_pair(&quot;장원준 &quot;, 3.05));
  pitcher_list.insert(std::make_pair(&quot;헥터 &quot;, 3.09));

  // 혹은 insert 를 안쓰더라도 [] 로 바로
  // 원소를 추가할 수 있습니다.
  pitcher_list[&quot;니퍼트&quot;] = 3.56;
  pitcher_list[&quot;박종훈&quot;] = 3.76;
  pitcher_list[&quot;켈리&quot;] = 3.90;

  print_map(pitcher_list);
  std::cout &lt;&lt; &quot;박세웅 방어율은? :: &quot; &lt;&lt; pitcher_list[&quot;박세웅&quot;] &lt;&lt; std::endl;
}
</code></pre>
<blockquote>
<p>니퍼트 3.56
박세웅 2.23
박종훈 3.76
장원준 3.05
차우찬 3.04
켈리 3.9
피어밴드 2.95
해커 2.93
헥터 3.09
박세웅 방어율은? :: 2.23</p>
</blockquote>
<h3 id="멀티셋multiset과-멀티맵multimap">멀티셋(multiset)과 멀티맵(multimap)</h3>
<p>셋과 맵과는 다르게 중복된 원소를 허락</p>
<ul>
<li><p>키와 값
  한 개의 키에 여러개의 값이 대응
  따라서 operator[] 사용 불가능</p>
</li>
<li><p>여러 키
equal_range 함수 사용
키에 대응되는 원소들의 반복자들중에서 
시작과 끝 바로 다음을 가리키는 반복자를 std::pair 객체로 리턴</p>
</li>
</ul>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;string&gt;

template &lt;typename K, typename V&gt;
void print_map(const std::multimap&lt;K, V&gt;&amp; m) {
  // 맵의 모든 원소들을 출력하기
  for (const auto&amp; kv : m) {
      std::cout &lt;&lt; kv.first &lt;&lt; &quot; &quot; &lt;&lt; kv.second &lt;&lt; std::endl;
  }
}

int main() {
  std::multimap&lt;int, std::string&gt; m;
  m.insert(std::make_pair(1, &quot;hello&quot;));
  m.insert(std::make_pair(1, &quot;hi&quot;));
  m.insert(std::make_pair(1, &quot;ahihi&quot;));
  m.insert(std::make_pair(2, &quot;bye&quot;));
  m.insert(std::make_pair(2, &quot;baba&quot;));

  print_map(m);
  std::cout &lt;&lt; &quot;--------------------&quot; &lt;&lt; std::endl;
  // 1 을 키로 가지는 반복자들의 시작과 끝을
  // std::pair 로 만들어서 리턴한다.
  auto range = m.equal_range(1);
  for (auto itr = range.first; itr != range.second; ++itr) {
      std::cout &lt;&lt; itr-&gt;first &lt;&lt; &quot; : &quot; &lt;&lt; itr-&gt;second &lt;&lt; &quot; &quot; &lt;&lt; std::endl;
  }
}
</code></pre>
<blockquote>
<p>1 hello
1 hi
1 ahihi
2 bye
2 baba
--------------------
1 : hello
1 : hi
1 : ahihi</p>
</blockquote>
<h3 id="정렬되지-않은-셋과-맵unordered_set-unordered_-map">정렬되지 않은 셋과 맵(unordered_set, unordered_-map)</h3>
<p>원소가 정렬되어 있지 않음
(셋이나 맵의 경우 원소들이 순서대로 정렬되어서 내부에 저장)
반복자로 원소 출력시 랜덤한 순서로 출력</p>
<ul>
<li><p>O(1)
insert, erase, find 모두
해시 함수로 구현 즉 최악은 O(n)
최적화가 매우 필요한 작업일 경우에만 
해시 함수를 잘 설계해서 사용해야함 </p>
</li>
<li><p>원하는 타입
  원하는 타입으로 사용하고자 하면 직접 해시 함수 구현</p>
</li>
</ul>
<h4 id="해시-함수hash-function">해시 함수(Hash function)</h4>
<p>  임의의 크기의 데이터를 고정된 크기의 데이터로 대응시켜주는 함수 
  1 부터 D (= 상자의 수)까지의 값을반환하고 그 해시값 (해시 함수로 계산한 값)을 원소를 저장
  같은 원소를 해시 함수에 전달한다면 같은 해시값을 리턴
<img src="https://velog.velcdn.com/images/choi_612/post/8e8ef6e0-32c3-4d05-bceb-0029a9d2b42f/image.png" alt=""></p>
<ul>
<li>해시 충돌(hash collision)
  다른 원소임에도 불구하고 같은 해시값을 갖는 경우</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;unordered_set&gt;

template &lt;typename K&gt;
void print_unordered_set(const std::unordered_set&lt;K&gt;&amp; m) {
  // 셋의 모든 원소들을 출력하기
  for (const auto&amp; elem : m) {
      std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

template &lt;typename K&gt;
void is_exist(std::unordered_set&lt;K&gt;&amp; s, K key) {
  auto itr = s.find(key);
  if (itr != s.end()) {
      std::cout &lt;&lt; key &lt;&lt; &quot; 가 존재!&quot; &lt;&lt; std::endl;
  } else {
      std::cout &lt;&lt; key &lt;&lt; &quot; 가 없다&quot; &lt;&lt; std::endl;
  }
} 

int main() {
  std::unordered_set&lt;std::string&gt; s;
  s.insert(&quot;hi&quot;);
  s.insert(&quot;my&quot;);
  s.insert(&quot;name&quot;);
  s.insert(&quot;is&quot;);
  s.insert(&quot;psi&quot;);
  s.insert(&quot;welcome&quot;);
  s.insert(&quot;to&quot;);
  s.insert(&quot;c++&quot;);

  print_unordered_set(s);
  std::cout &lt;&lt; &quot;----------------&quot; &lt;&lt; std::endl;
  is_exist(s, std::string(&quot;c++&quot;));
  is_exist(s, std::string(&quot;c&quot;));
  std::cout &lt;&lt; &quot;----------------&quot; &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;&#39;hi&#39; 를 삭제&quot; &lt;&lt; std::endl;
  s.erase(s.find(&quot;hi&quot;));
  is_exist(s, std::string(&quot;hi&quot;));
  }</code></pre>
<blockquote>
<p>c++
to
my
name
hi
is
psi
welcome
---------------
c++ 가 존재!
c 가 없다
---------------
&#39;hi&#39; 를 삭제
hi 가 없다</p>
</blockquote>
<h1 id="c-표준-알고리즘-라이브러리">C++ 표준 알고리즘 라이브러리</h1>
<p>여러 함수로 작업</p>
<p>두종류</p>
<p>알고리즘을 수행할 반복자의 시작점과 끝점 바로 뒤를 인자로 받음 
<code>template &lt;typename Iter&gt; void do_something(Iter begin, Iter end);</code></p>
<p>반복자는 동일하나 ’특정한 조건’ 을 추가 인자를 받음
<code>template &lt;typename Iter, typename Pred&gt; void do_something(Iter begin, Iter end, Pred pred);</code></p>
<p>’특정한 조건’ - 서술자(Predicate)
 Pred 에는 보통 bool 을 리턴하는 함수객체(Functor)</p>
<h2 id="정렬-sort-stable_sort-partial_sort">정렬 (sort, stable_sort, partial_sort)</h2>
<ul>
<li>sort
  일반적인 정렬 함수라 생각하시면 됩니다.</li>
<li>stable_sort 
  정렬을 하되 원소들 간의 순서를 보존</li>
<li>partial_sort 
  배열의 일부분만 정렬합니다 </li>
</ul>
<h3 id="sort">sort</h3>
<p>오름차순으로 정렬
<code>sort(vec.begin(), vec.end());</code></p>
<p>임의접근 반복자(RandomAccessIterator) 타입만 가능
즉 벡터와 데크만 가능</p>
<p>내림 차순으로 정렬을 원하면 
compare함수 생성하여 사용
greater 사용도 가능 
<code>std::sort(vec.begin(), vec.end(), greater&lt;int&gt;());</code></p>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;iostream&gt;
#include &lt;vector&gt;

template &lt;typename Iter&gt;
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout &lt;&lt; *begin &lt;&lt; &quot; &quot;;
    begin++;
  }
  std::cout &lt;&lt; std::endl;
}

struct int_compare {
  bool operator()(const int&amp; a, const int&amp; b) const { return a &gt; b; }
};
int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(5);
  vec.push_back(3);
  vec.push_back(1);
  vec.push_back(6);
  vec.push_back(4);
  vec.push_back(7);
  vec.push_back(2);
  std::cout &lt;&lt; &quot;정렬 전 ----&quot; &lt;&lt; std::endl;
  print(vec.begin(), vec.end());
  std::sort(vec.begin(), vec.end(), int_compare());
  std::cout &lt;&lt; &quot;정렬 후 ----&quot; &lt;&lt; std::endl;
  print(vec.begin(), vec.end());
}</code></pre>
<blockquote>
<p>정렬 전 ----
5 3 1 6 4 7 2
정렬 후 ----
7 6 5 4 3 2 1</p>
</blockquote>
<h3 id="partial_sort">partial_sort</h3>
<p>정렬을 [stard, end) 전체 원소들 중에서 [start, middle) 까지 원소들이 전체 원소들 중에서 제일 작은애들 순으로 정렬
<code>std::partial_sort(vec.begin(), vec.begin() + 3, vec.end());</code></p>
<h3 id="stable_sort">stable_sort</h3>
<p>원소가 삽입되어 있는 순서를 보존하는 정렬
<code>std::stable_sort(vec2.begin(), vec2.end());</code></p>
<h2 id="원소-제거-remove-remove_if">원소 제거 (remove, remove_if)</h2>
<p>컨테이너의 원소를 제거하는 함수</p>
<h3 id="erase">erase</h3>
<p>vector의 erase
pos 가 가리키는 원소를 벡터에서 지움
<code>Iterator erase(Iterator pos);</code>
first 부터 last 사이에 있는 모든 원소들을 지움
<code>Iterator erase(Iterator first, Iterator last);</code></p>
<h3 id="remove">remove</h3>
<p>원하는 값을 가진 원소들을 컨테이너 뒤로 보내 지움
즉 지우지 말아야할 원소를 하나씩 앞으로 이동시키는 방식
크기가 지우는게 아니라 땡기는 거다. &lt; 크기가 그대로임
대신 한번에 원하는 값을 갖는 원소 여러개 지우기 가능
보통 erase와 조합하여 사용
시작부터 끝전까지 값 찾아 제거
<code>remove(vec.begin(), vec.end(), 3)</code>
<code>vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());</code></p>
<h3 id="remove_if">remove_if</h3>
<p>시작부터 끝전까지 조건 충족하면 제거
<code>vec.erase(remove_if(vec.begin(), vec.end(), is_odd()), vec.end());</code></p>
<p>조건을 위한 함수 객체 사용시 유의
※함수 객체 사용시 주의
객체 내부 변수를 통해절대로 특정 상태를 저장해서 이에 따라 결과가 달라지는 루틴을 짜면 안됨 
-&gt; 단순 전달이 아닌 복사나 소멸로 값이 다르게 파악됨</p>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename Iter&gt;
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout &lt;&lt; &quot;[&quot; &lt;&lt; *begin &lt;&lt; &quot;] &quot;;
    begin++;
  }
  std::cout &lt;&lt; std::endl;
}

struct is_odd {
  bool operator()(const int&amp; i) { return i % 2 == 1; }
};

int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(5);
  vec.push_back(3);
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);
  vec.push_back(4);

  std::cout &lt;&lt; &quot;처음 vec 상태 ------&quot; &lt;&lt; std::endl;
  print(vec.begin(), vec.end());
  std::cout &lt;&lt; &quot;벡터에서 홀수 인 원소 제거 ---&quot; &lt;&lt; std::endl;
  vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd()), vec.end());
  print(vec.begin(), vec.end());
}</code></pre>
<blockquote>
<p>처음 vec 상태 ------
[5] [3] [1] [2] [3] [4]
벡터에서 홀수 인 원소 제거 ---
[2] [4]</p>
</blockquote>
<h2 id="람다-함수lambda-function">람다 함수(lambda function)</h2>
<p>임시적으로 함수 객체 생성</p>
<ul>
<li>람다 함수도 함수이므로 자기 자신만의 스코프를 갖음</li>
<li>캡쳐 목록(capture list)
  외부 변수를 캡쳐 리스트로 접근 가능 
  만약 객체 멤버변수를 원하면 해당 객체를 캡쳐(this처럼)
  []
  아무것도 캡쳐 안함
  [&amp;a, b]
  a 는 레퍼런스로 캡쳐하고 b 는 (변경 불가능한) 복사본으로 캡쳐
  [&amp;] 
  외부의 모든 변수들을 레퍼런스로 캡쳐
  [=] 
  외부의 모든 변수들을 복사본으로 캡쳐</li>
</ul>
<p>일반적인 꼴
<code>[capture list] (받는 인자) -&gt; 리턴 타입 { 함수 본체 }</code>
리턴 타입 생략
<code>[capture list] ( 받는 인자) {함수 본체}</code></p>
<p>example code</p>
<pre><code class="language-cpp">//함수 객체
struct is_odd {
  bool operator()(const int&amp; i) { return i % 2 == 1; }
};

//람다 함수
[](int i) -&gt; bool { return i % 2 == 1; }

// 바로 호출
[](int i) { return i % 2 == 1; }(3);// true

//함수객체 성성 후 호출
auto func = [](int i) { return i % 2 == 1; };
func(4); // false;

//캡쳐 리스트 사용
[&amp;num_erased](int i) {
  if (num_erased &gt;= 2)
    return false;
  else if (i % 2 == 1) {
    num_erased++;
    return true;
  }
  return false;
}

// 객체를 캡쳐 리스트로 사용
vec.erase(std::remove_if(vec.begin(), vec.end(),
  [this](int i) {
    if (this-&gt;num_erased &gt;= 2)
        return false;
    else if (i % 2 == 1) {
      this-&gt;num_erased++;
      return true;
    }
    return false;
    }),
  vec.end());</code></pre>
<h2 id="원소-수정하기-transform">원소 수정하기 (transform)</h2>
<p>transform
원소들을 수정하는 함수
컨테이너 전체 혹은 일부를 순회하면서 값들을 수정
 <code>transform (시작 반복자, 끝 반복자, 결과를 저장할 컨테이너의 시작 반복자, Pred)</code></p>
<ul>
<li>저장하는 컨테이너의 크기가 원래의 컨테이너보다 최소한 같거나 커야함</li>
</ul>
<p>example code</p>
<pre><code>std::transform(vec.begin(), vec.end(), vec.begin(),[](int i) { return i + 1; });</code></pre><h2 id="원소를-탐색하는-함수find-find_if-any_of-all_of">원소를 탐색하는 함수(find, find_if, any_of, all_of...)</h2>
<p>원소들을 탐색하는 계열의 함수</p>
<h3 id="find">find</h3>
<p>first 부터 last 까지 순회하며 value와 같은 값 파악 있다면 이를 가리키는 반복자를 리턴
<code>template &lt;class InputIt, class T&gt;
InputIt find(InputIt first, InputIt last, const T&amp; value)</code></p>
<ul>
<li>중복되는 값이 있더라도 가장 먼저 찾은 것을 리턴</li>
<li>중복 되는 값 찾기
  마지막으로 찾은 위치 바로 다음 부터 계속 순차적으로 탐색<br>  <code>current = find(current, vec.end(), 3);</code></li>
<li>만약 해당 컨테이너에 find가 있다면 해당 컨테이너 find 사용</li>
</ul>
<h3 id="find_if">find_if</h3>
<p>함수 객체를 인자로 받아서 그 결과가 참인 원소들을 탐색
<code>current = std::find_if(current, vec.end(), [](int i) { return i % 3 == 2; });</code></p>
<h3 id="any_of">any_of</h3>
<p>인자로 받은 범위안의 모든 원소들 중에서 조건을 하나라도 충족하는 것이 있다면 true 리턴
OR 연산과 비슷
<code>std::any_of(users.begin(), users.end(),
[](User&amp; user) { return user.level &gt;= 19; });</code></p>
<h3 id="all_of">all_of</h3>
<p>모든 원소들이 전부 조건을 충족해야 true 리턴
AND 연산과 비슷
<code>std::all_of(users.begin(), users.end(),
[](User&amp; user) { return user.level &gt;= 15; });</code></p>
<h1 id="c-문자열string-과-string_view">C++ 문자열(string 과 string_view)</h1>
<h2 id="basic_string">basic_string</h2>
<p>basic_string 은 CharT 타입의 객체들을 메모리에 연속적으로 저장하고, 여러가지 문자열 연산들을 지원해주는 클래스</p>
<ul>
<li><p>여러 종류 타입으로 인스턴스화된 문자열
  char, wchar_t, char8_t, char16_t, char32_t</p>
</li>
<li><p>basic_sting
  문자열들을 어떻게 보관하는지에 대한 로직</p>
</li>
<li><p>Traits
  문자열들을 어떻게 연산하는지 에 대한 로직</p>
</li>
<li><p>std::char_traits 사용 시
  사용자가 원하는 기능을 가진 문자열을 생성 가능</p>
</li>
</ul>
<h2 id="짧은-문자열-최적화-sso">짧은 문자열 최적화 (SSO)</h2>
<p>짧은 길이 문자열의 경우 따로 문자 데이터를 위한 메모리를
할당하는 대신에 그냥 객체 자체에 저장</p>
<h2 id="리터럴-연산자">리터럴 연산자</h2>
<p>문자열 리터럴로 정의 </p>
<p><code>std::string operator&quot;&quot; s(const char *str, std::size_t len);</code>
<code>std::string str = &quot;hello&quot;; // char[]</code></p>
<h2 id="c-에서-한글-다루기">C++ 에서 한글 다루기</h2>
<p>인코딩(Encoding) 방식 사용
인코딩 방식에 따라 컴퓨터에서 문자를 표현하기 위해 
동일하게 4 바이트를 사용하는 대신에, 
어떤 문자는 1 바이트, 2 바이트 등의 다른 길이로 저장</p>
<p>인코딩 방식</p>
<ul>
<li>UTF-8
  문자를 최소 1 부터 최대 4 바이트로 표현
  즉 문자마다 길이가 다르다!</li>
<li>UTF-16 
  문자를 2 혹은 4 바이트로 표현</li>
<li>UTF-32 
  문자를 4 바이트로 표현
  메모리 사용량 매우 증가</li>
</ul>
<h2 id="string_view">string_view</h2>
<p>문자열을 읽기 만 하는 클래스
어딘가 존재하는 문자열을 참조해서 읽기만 함</p>
<ul>
<li>메모리 할당 불필요
  읽고 있는 문자열의 시작 주소값만 복사</li>
<li>원본 문자열을 수정하지 않는 연산들 사용 가능</li>
<li>불필요한 복사 발생 하지 않음
  const char* 과 const string&amp;사이에서 깔끔하게 처리</li>
<li>Undefined behavior 주의 
  현재 보고 있는 문자열이 소멸된다면 정의되지 않은 작업이 발생 
  살아 있는 문자열인지 확인이 필요</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 템플릿 ]]></title>
            <link>https://velog.io/@choi_612/CPP6</link>
            <guid>https://velog.io/@choi_612/CPP6</guid>
            <pubDate>Tue, 31 May 2022 10:26:42 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>10장 c++ 템플릿 410p-493p</p>
<hr>
<h2 id="template">template</h2>
<p>원하는 타입으로 대체 되어 사용됨 </p>
<h3 id="클래스-템플릿">클래스 템플릿</h3>
<p>정의되는 클래스에 대해 템플릿을 정의하고, 템플릿 인자로 T를 받아 해당 타입인 것을 명시 
<code>template &lt;typename T&gt;</code>
<code>template &lt;class T&gt;</code></p>
<ul>
<li>클래스 템플릿 인스턴스화 (class template instantiation)
  클래스 템플릿에 인자를 전달해서 실제 코드를 생성하는 것</li>
</ul>
<p>example code </p>
<pre><code class="language-cpp">template &lt;typename T&gt;
class Vector {
    T* data;
    int capacity;
    int length;
    public:
    // 생성자
        Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}
    // 맨 뒤에 새로운 원소를 추가한다.
        void push_back(T s) {
            if (capacity &lt;= length) {
                T* temp = new T[capacity * 2];
                for (int i = 0; i &lt; length; i++) {
                    temp[i] = data[i];
                }
                delete[] data;
                data = temp;
                capacity *= 2;
            }
            data[length] = s;
            length++;
        }
    // 임의의 위치의 원소에 접근한다.
    T operator[](int i) { return data[i]; }
    // x 번째 위치한 원소를 제거한다.
    void remove(int x) {
        for (int i = x + 1; i &lt; length; i++) {
            data[i - 1] = data[i];
        }
        length--;
    }    
    // 현재 벡터의 크기를 구한다.
    int size() { return length; }
        ~Vector() {
        if (data) {
            delete[] data;
        }
    }
};


int main() {
  // int 를 보관하는 벡터를 만든다.
  Vector&lt;int&gt; int_vec;
  int_vec.push_back(3);
  int_vec.push_back(2);

  std::cout &lt;&lt; &quot;-------- int vector ----------&quot; &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;첫번째 원소 : &quot; &lt;&lt; int_vec[0] &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;두번째 원소 : &quot; &lt;&lt; int_vec[1] &lt;&lt; std::endl;

  Vector&lt;std::string&gt; str_vec;
  str_vec.push_back(&quot;hello&quot;);
  str_vec.push_back(&quot;world&quot;);

  std::cout &lt;&lt; &quot;-------- std::string vector -------&quot; &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;첫번째 원소 : &quot; &lt;&lt; str_vec[0] &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;두번째 원소 : &quot; &lt;&lt; str_vec[1] &lt;&lt; std::endl;</code></pre>
<blockquote>
<p>-------- int vector ----------
첫번째 원소 : 3
두번째 원소 : 2
-------- std::string vector -------
첫번째 원소 : hello
두번째 원소 : world</p>
</blockquote>
<h3 id="템플릿-특수화-template-specialization">템플릿 특수화 (template specialization)</h3>
<p>특정 경우에 대해서 따로 처리하는 것</p>
<ul>
<li>템플릿 부분 특수화 시에 반드시 다른 연산자가 붙지 않고 단순한 식별자만 입력해야함</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename A, typename B, typename C&gt;
class test {};

template &lt;typename B&gt;
class test&lt;int, B, double&gt; {};

template &lt;&gt;
class test&lt;int, int, double&gt; {};

//전달하는 템플릿 인자가 없어도 특수화를 원할시 template &lt;&gt;
template &lt;&gt;
class Vector&lt;bool&gt; {
... // 원하는 코드
}
</code></pre>
<h3 id="함수-템플릿-function-template">함수 템플릿 (Function template)</h3>
<p>함수 템플릿</p>
<p>원하는 타입을 T로 처리 이후 인스턴스화를 통해 사용
클래스를 인스턴스화 했을 때 와는 다르게 &lt;&gt; 사용하지 않음
들어온 파라미터의 타입을 파악하여 인스턴스</p>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
  T max(T&amp; a, T&amp; b) {
  return a &gt; b ? a : b;
}
int main() {
  int a = 1, b = 2;

  std::cout &lt;&lt; &quot;Max (&quot; &lt;&lt; a &lt;&lt; &quot;,&quot; &lt;&lt; b &lt;&lt; &quot;) ? : &quot; &lt;&lt; max(a, b) &lt;&lt; std::endl;
  std::string s = &quot;hello&quot;, t = &quot;world&quot;;
  std::cout &lt;&lt; &quot;Max (&quot; &lt;&lt; s &lt;&lt; &quot;,&quot; &lt;&lt; t &lt;&lt; &quot;) ? : &quot; &lt;&lt; max(s, t) &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>Max (1,2) ? : 2
Max (hello,world) ? : world</p>
</blockquote>
<h3 id="함수-객체function-object---functor">함수 객체(Function Object - Functor)</h3>
<p>함수는 아니지만 함수 인 척을 하는 객체</p>
<p>example code</p>
<pre><code class="language-cpp">
struct Comp1 {
    bool operator()(int a, int b) { return a &gt; b; }
};

struct Comp2 {
    bool operator()(int a, int b) { return a &lt; b; }
};

template &lt;typename Cont, typename Comp&gt;
void bubble_sort(Cont&amp; cont, Comp&amp; comp) {
  for (int i = 0; i &lt; cont.size(); i++) {
    for (int j = i + 1; j &lt; cont.size(); j++) {
    //comp가 객체인데 함수처럼 쓰임
      if (!comp(cont[i], cont[j])) {
      cont.swap(i, j);
      }
    }
  }
}

// 사용 예제

Comp2 comp2;
bubble_sort(int_vec, comp2);
</code></pre>
<h3 id="타입이-아닌-템플릿-인자-non-type-template-arguments">타입이 아닌 템플릿 인자 (non-type template arguments)</h3>
<p>하나의 인자로 템플릿을 통해 넘김</p>
<ul>
<li><p>템플릿 인자로 전달할 수 있는 타입들</p>
<ul>
<li>정수 타입들 (bool, char, int, long 등등). 당연히 float 과 double 은 제외</li>
<li>포인터 타입</li>
<li>enum 타입</li>
<li>std::nullptr_t (널 포인터)</li>
</ul>
</li>
<li><p>컴파일 타입에 값들이 정해져야 하는 것들를 주로 사용 </p>
</li>
<li><p>템플릿 인자로 배열의 크기를 명시하여 함수에 배열을 전달할 때 배열의 크기에 대한 정보 확인</p>
</li>
<li><p>의존 타입(dependent type) 
  템플릿 인자에 따라서 어떠한 타입이 달라질수 있음</p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename T, int num&gt;
T add_num(T t) {
    return t + num;
}

int main() {
  int x = 3;
  std::cout &lt;&lt; &quot;x : &quot; &lt;&lt; add_num&lt;int, 5&gt;(x) &lt;&lt; std::endl;
}
</code></pre>
<p>template 의 인자로 T 를 받고, 추가적으로 마치 함수의 인자 처럼 int num 사용 </p>
<blockquote>
<p>x : 8</p>
</blockquote>
<h3 id="디폴트-템플릿-인자">디폴트 템플릿 인자</h3>
<p>디폴트로 바뀌지 않는 템플릿 인자 
= 통해 디폴트 값 지정
<code>template &lt;typename T, int num = 5&gt;</code></p>
<h3 id="가변-길이-템플릿">가변 길이 템플릿</h3>
<h4 id="템플릿-파리미터-팩parameter-pack">템플릿 파리미터 팩(parameter pack)</h4>
<p>0 개 이상의 템플릿 인자들을 가변 길이로 받아 드림</p>
<p><code>template &lt;typename T, typename... Types&gt;</code></p>
<ul>
<li>템플릿 파라미터 팩과 함수 파라미터 팩의 차이점
  템플릿의 경우 타입 앞 에 ... 
  함수의 경우 타입 뒤 에 ...</li>
<li>sizeof...()
  전체 인자의 개수 리턴</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void print(T arg) {
    std::cout &lt;&lt; arg &lt;&lt; std::endl;
}

template &lt;typename T, typename... Types&gt;
void print(T arg, Types... args) {
  std::cout &lt;&lt; arg &lt;&lt; &quot;, &quot;;
  print(args...);
}

int main() {
  print(1, 3.1, &quot;abc&quot;);
  print(1, 2, 3, 4, 5, 6, 7);
}</code></pre>
<blockquote>
<p>1, 3.1, abc
1, 2, 3, 4, 5, 6, 7</p>
</blockquote>
<h4 id="fold-expression">Fold Expression</h4>
<p><code>return (... + nums);</code> 
해당 형식을 컴파일러가 아래 처럼 해석
<code>return ((((1 + 4) + 2) + 3) + 10);</code></p>
<ul>
<li>단항 좌측 Fold (Unary left fold) 4가지 존재 
<img src="https://velog.velcdn.com/images/choi_612/post/98b05530-9349-468c-b104-42f5d571ab72/image.png" alt=""></li>
</ul>
<h2 id="템플릿-메타-프로그래밍">템플릿 메타 프로그래밍</h2>
<p><strong>Template Meta Programming - TMP</strong>
타입을 가지고 컴파일 타임에 생성되는 코드로 짜는 프로그래밍</p>
<ul>
<li><p>컴파일 타임에 모든 연산이 끝나기 때문에 프로그램 실행 속도를 향상 시킬 수 있다는 장점</p>
</li>
<li><p>템플릿 메타 프로그래밍은 매우 복잡하니 유의</p>
</li>
<li><p>TMP 를 통해서 컴파일 타임에 debugging 가능 (Ex. 단위나 통화 일치 여부등등) </p>
</li>
<li><p>런타임에서 찾아야 하는 오류를 컴파일 타임에서 미리 다 잡을 수 있음</p>
</li>
<li><p>속도가 매우 중요한 프로그램의 경우 TMP 를 통해서 런타임 속도 향상 가능</p>
</li>
<li><p>단위(Unit) 라이브러리
  세분화하여 타입을 정의하고 계산할때 TMP사용 </p>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp"> /* 컴파일 타임 팩토리얼 계산 */
#include &lt;iostream&gt;

template &lt;int N&gt;
struct Factorial {
    static const int result = N * Factorial&lt;N - 1&gt;::result;
};

template &lt;&gt;
struct Factorial&lt;1&gt; {
    static const int result = 1;
};

int main() {
  std::cout &lt;&lt; &quot;6! = 1*2*3*4*5*6 = &quot; &lt;&lt; Factorial&lt;6&gt;::result &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>6! = 1<code>*</code>2<code>*</code>3<code>*</code>4<code>*</code>5<code>*</code>6 = 720</p>
</blockquote>
<h2 id="tips">Tips</h2>
<h3 id="bool-처리">bool 처리</h3>
<p>bool 1 bytes &lt; int 4bytes를 bool 32bits로 사용하여 효율적
N 번째 bool 데이터는 N / 32 번째 int에 존재, N % 32 번째 비트</p>
<h3 id="or-연산">or 연산</h3>
<p>특정 비트에만 선택적으로 1로 변환으로 특화 
주변 나머지 비트들의 값은 보존하면서 특정 비트만 1</p>
<h3 id="and-연산">and 연산</h3>
<p>원하는 위치의 1 을 AND 하게 되면 해당 비트의 값이 무엇 파악 가능 
해당 위치에 있는 비트가 1 일 때 에만 저 값이 0 이 아니게 되고 0
이면 저 값 전체가 0</p>
<h3 id="typedef">typedef</h3>
<p>자기 자신을 가리키는 타입</p>
<h3 id="using">using</h3>
<p>자기 자신을 가리키는 타입
typedef와 같은 역할로 보다 직관적 </p>
<pre><code class="language-cpp">typedef Ratio_add&lt;rat, rat2&gt; rat3;
using rat3 = Ratio_add&lt;rat, rat2&gt;;</code></pre>
<h3 id="auto-키워드">auto 키워드</h3>
<p>컴파일러가 타입을 정확히 알아낼 수 있는 경우 굳이 그 길고 긴 타입을 적지 않고 간단히 auto 로 표현</p>
<p>example code</p>
<pre><code class="language-cpp">
int sum(int a, int b) { return a + b; }

class SomeClass {
  int data;
  public:
    SomeClass(int d) : data(d) {}
    SomeClass(const SomeClass&amp; s) : data(s.data) {}
};

int main() {
  // 함수 리턴 타입으로 부터 int 라고 추측 가능
  auto c = sum(1, 2); 
  // double 로 추측 가능!
  auto num = 1.0 + 2.0;

  // 해당 객체로 추측
  SomeClass some(10);
  auto some2 = some;

  //그냥 int라 추측됨
  auto some3(10); 

    std::cout &lt;&lt; &quot;c 의 타입은? :: &quot; &lt;&lt; typeid(c).name() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;num 의 타입은? :: &quot; &lt;&lt; typeid(num).name() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;some2 의 타입은? :: &quot; &lt;&lt; typeid(some2).name() &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;some3 의 타입은? :: &quot; &lt;&lt; typeid(some3).name() &lt;&lt; std::endl;
}
</code></pre>
<blockquote>
<p>c 의 타입은? :: i
num 의 타입은? :: d
some2 의 타입은? :: 9SomeClass
some3 의 타입은? :: i</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[클래스 상속 (2)]]></title>
            <link>https://velog.io/@choi_612/CPP5</link>
            <guid>https://velog.io/@choi_612/CPP5</guid>
            <pubDate>Tue, 31 May 2022 10:20:45 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>7장 클래스의 상속 298p - 339p</p>
<hr>
<h2 id="상속의-관계">상속의 관계</h2>
<h3 id="장점">장점</h3>
<ul>
<li>추상화</li>
<li>특수화</li>
<li>일반화</li>
<li>다형성
  하나의 메소드를 호출했음에도 불구하고 여러가지 다른
작업들을 수행</li>
</ul>
<h3 id="is---a--has---a">is - a / has - a</h3>
<p><img src="https://velog.velcdn.com/images/choi_612/post/73518bd9-851e-4210-a792-8a865d79f2f5/image.png" alt=""></p>
<p>is - a / has - a
모든 상속 관계 
뒤 바꾸면 성립 안됨
기반 클래스의 모든 기능을 파생 클래스가 포함
파생 is a 기반</p>
<h2 id="up-casting">up casting</h2>
<p>파생 클래스에서 기반 클래스로 캐스팅 하는 것</p>
<p>example code </p>
<pre><code class="language-cpp">class Base {
    std::string s;

    public:
        Base() : s(&quot;기반&quot;) { std::cout &lt;&lt; &quot;기반 클래스&quot; &lt;&lt; std::endl; }
        void what() { std::cout &lt;&lt; s &lt;&lt; std::endl; }
};

class Derived : public Base {
    std::string s;

    public:
        Derived() : s(&quot;파생&quot;), Base() { std::cout &lt;&lt; &quot;파생 클래스&quot; &lt;&lt; std::endl; }
        void what() { std::cout &lt;&lt; s &lt;&lt; std::endl; }
};

int main() {
    Base p;
    Derived c;

    std::cout &lt;&lt; &quot;=== 포인터 버전 ===&quot; &lt;&lt; std::endl;
    // 포인터 사용
    Base* p_c = &amp;c;
    p_c-&gt;what();

    return 0;
}</code></pre>
<blockquote>
<p>기반 클래스
기반 클래스
파생 클래스
=== 포인터 버전 ===
기반</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choi_612/post/6e59e6e0-e6f8-425b-9e2a-f80e2f10bf6c/image.png" alt=""></p>
<p>Derived가 Base를 상속 받았으므로<br>Base 객체를 가리키는 포인터가 Derived c객체를 가리켜도 무방
단 p는 Base 객체를 가르키는 포인터 이므로 Base class에 있는 것만 사용 </p>
<p>※ 코드에서 자주 보인다 
사용하는 포인터가 뭘 가르키는지 확인 유의</p>
<h2 id="down-casting">down casting</h2>
<p>기반 클래스에서 파생 클래스로 캐스팅 하는 것</p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/8738fd74-3438-400c-824c-8ad9237344d8/image.png" alt=""></p>
<p><strong>하지만</strong> 컴파일 에러 발생 
static_cast를 사용하여도 런타임 에러 발생 </p>
<h3 id="dynamic_cast">dynamic_cast</h3>
<p>부모 클래스의 포인터에서 자식 클래스의 포인터로 다운 캐스팅 해주는 연산자</p>
<p>불가능</p>
<ul>
<li>부모클래스의 생성자로 생성되었고 부모 포인터가 가리키고 있는 클래스를
자식클래스 포인터로</li>
</ul>
<p>가능</p>
<ul>
<li>자식클래스의 생성자로 생성되었고 부모클래스 포인터가 가리키고 있는 클래스를 
자식 클래스 포인터로 </li>
</ul>
<h2 id="virtual">virtual</h2>
<p>가상 함수(virtual function)</p>
<ul>
<li>런타임시 각 생성된 클래스에 맞는 함수로 실행하게 됨 </li>
<li>기반클래스에서 virtual로 정의</li>
<li>파생 클래스의 함수가 기반 클래스의 함수를 오버라이드 하기 위해서는 두 함수의 꼴이 정확히 같아야함 </li>
<li>명시적으로 override도 가능 overide 사용</li>
<li>가상 함수를 사용하게 되면 약간의 오버헤드 (overhead) 가 존재</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">
class Base {
    public:
        Base() { std::cout &lt;&lt; &quot;기반 클래스&quot; &lt;&lt; std::endl; }
        virtual void what() { std::cout &lt;&lt; &quot;기반 클래스의 what()&quot; &lt;&lt; std::endl; }
};

class Derived : public Base {
    public:
        Derived() : Base() { std::cout &lt;&lt; &quot;파생 클래스&quot; &lt;&lt; std::endl; }
        void what() { std::cout &lt;&lt; &quot;파생 클래스의 what()&quot; &lt;&lt; std::endl; }

        /*
        void what() override{ std::cout &lt;&lt; &quot;파생 클래스의 what()&quot; &lt;&lt; std::endl; }
        */
};

int main() {
    Base p;
    Derived c;

    Base* p_c = &amp;c;
    Base* p_p = &amp;p;

    std::cout &lt;&lt; &quot; == 실제 객체는 Base == &quot; &lt;&lt; std::endl;
    p_p-&gt;what();

    std::cout &lt;&lt; &quot; == 실제 객체는 Derived == &quot; &lt;&lt; std::endl;
    p_c-&gt;what();

    return 0;
}</code></pre>
<blockquote>
<p>기반 클래스
기반 클래스
파생 클래스
== 실제 객체는 Base ==
기반 클래스의 what()
== 실제 객체는 Derived ==
파생 클래스의 what()</p>
</blockquote>
<h3 id="virtual-소멸자">virtual 소멸자</h3>
<p>상속 시에, 부모 클래스 소멸자를 가상함수로 만들어야 함</p>
<p><strong>아니면</strong>
Parent 포인터로 Child 가리켰을 때 
Parent *p = new Child();
child 소멸자가 안 불려서 memory leak 발생</p>
<h3 id="pure-virtual-function">pure virtual function</h3>
<p>무엇을 하는지 정의되어 있지 않는 함수
파생 클래스에서 반드시 오버라이딩 되어야만 하는 함수
<code>virtual void func() = 0;</code></p>
<p>example code</p>
<pre><code class="language-cpp">class Animal {
    public:
        Animal() {}
        virtual ~Animal() {}
        virtual void speak() = 0;
};

class Dog : public Animal {
    public:
        Dog() : Animal() {}
        void speak() override { std::cout &lt;&lt; &quot;왈왈&quot; &lt;&lt; std::endl; }
};

class Cat : public Animal {
    public:
        Cat() : Animal() {}
        void speak() override { std::cout &lt;&lt; &quot;야옹야옹&quot; &lt;&lt; std::endl; }
};

int main() {
    Animal* dog = new Dog();
    Animal* cat = new Cat();
    dog-&gt;speak();
    cat-&gt;speak();
}</code></pre>
<blockquote>
<p>왈왈
야옹야옹</p>
</blockquote>
<h3 id="abstract-class">abstract class</h3>
<p>순수 가상 함수를 최소 한개 포함하여 반드시 상속 되어야 하는 클래스</p>
<h3 id="multiple-inheritance">multiple inheritance</h3>
<p>다중 상속
한 클래스가 다른 여러 개의 클래스들을 상속 받음</p>
<ul>
<li>상속 순서에 따라 생성자 호출 순서가 달라짐</li>
<li>기반 클래스들에 겹치는 멤버 변수나 함수가 있으면 구분 불가능 &gt; 에러</li>
<li>다이아몬드 상속(diamond inheritance)주의 &gt; virtual 상속을 통해 해결 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/choi_612/post/4dc989c8-2162-4987-958e-cd6c49a7a87a/image.png" alt=""></p>
<p>example code</p>
<pre><code class="language-cpp">
class A {
    public:
        int a;
        A() { std::cout &lt;&lt; &quot;A 생성자 호출&quot; &lt;&lt; std::endl; }
};

class B {
    public:
        int b;
        B() { std::cout &lt;&lt; &quot;B 생성자 호출&quot; &lt;&lt; std::endl; }
};

class C : public A, public B {
    public:
        int c;
        C() : A(), B() { std::cout &lt;&lt; &quot;C 생성자 호출&quot; &lt;&lt; std::endl; }
};

int main() { C c; }
</code></pre>
<blockquote>
<p>A 생성자 호출
B 생성자 호출
C 생성자 호출</p>
</blockquote>
<h3 id="class-디자인">class 디자인</h3>
<ul>
<li><p>브릿지 패턴
  N + M 개의 클래스 생성</p>
</li>
<li><p>중접된 일반화 
  N ×M 개의 파생 클래스 생성</p>
</li>
<li><p>다중 상속
  N ×M 개의 파생 클래스 생성</p>
</li>
</ul>
<h3 id="reference">reference</h3>
<p>포인터 대신 reference 사용가능 </p>
<p>example code</p>
<pre><code class="language-cpp">class A {
    public:
        virtual void show() { std::cout &lt;&lt; &quot;Parent !&quot; &lt;&lt; std::endl; }
};

class B : public A {
    public:
        void show() override { std::cout &lt;&lt; &quot;Child !&quot; &lt;&lt; std::endl; }
};

void test(A&amp; a) { a.show(); }

int main() {
    A a;
    B b;

    test(a);
    test(b);

    return 0;
}</code></pre>
<blockquote>
<p>Parent !
Child !</p>
</blockquote>
<p>test함수 래퍼런스로 파라미터를 받아 virtual로 파생클래스의 show함수를 받는다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클래스 상속 (1)]]></title>
            <link>https://velog.io/@choi_612/CPP4</link>
            <guid>https://velog.io/@choi_612/CPP4</guid>
            <pubDate>Mon, 30 May 2022 10:36:28 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>7장 클래스의 상속 270p - 297p</p>
<hr>
<h2 id="상속inheritance">상속(Inheritance)</h2>
<p>다른 클래스의 내용을 그대로 포함할 수 있는 작업을 가능하도록 함 </p>
<p>상속을 통해 다른 클래스의 정보를 물려 받아서 사용 가능 </p>
<ul>
<li>부모, 자식 클래스/ 기반, 파생 클래스
상속 받는 클래스 :  자식 / 파생</li>
</ul>
<h3 id="example-code---base--derived">example code - Base / Derived</h3>
<pre><code class="language-cpp">class Base {
    std::string s;
    public:
        Base() : s(&quot;기반&quot;) { std::cout &lt;&lt; &quot;기반 클래스&quot; &lt;&lt; std::endl; }
        void what() { std::cout &lt;&lt; s &lt;&lt; std::endl; }
};

class Derived : public Base {
    std::string s;
    public:
        Derived() : Base(), s(&quot;파생&quot;) {
            std::cout &lt;&lt; &quot;파생 클래스&quot; &lt;&lt; std::endl;
            // Base 에서 what() 을 물려 받았으므로
            // Derived 에서 당연히 호출 가능하다
            what();
        }
};

int main() {
    std::cout &lt;&lt; &quot; === 기반 클래스 생성 ===&quot; &lt;&lt;         std::endl;
    Base p;
    std::cout &lt;&lt; &quot; === 파생 클래스 생성 ===&quot; &lt;&lt;         std::endl;
    Derived c;
    return 0;
}
</code></pre>
<blockquote>
<p>=== 기반 클래스 생성 ===
기반 클래스
=== 파생 클래스 생성 ===
기반 클래스
파생 클래스
기반</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choi_612/post/d254a744-9eaa-408a-8bc4-7a613edaa8d6/image.png" alt=""></p>
<ul>
<li>Derived 생성자는 초기화 리스트에서 기반의 생성자를 호출해서 기반의 생성을 먼저 처리<ul>
<li>파생 클래스 가 출력하기 이전에 Base 의 생성자가 호출되어서 기반 클래스 가 먼저 출력</li>
</ul>
</li>
<li>What()<ul>
<li>Base 의 모든 정보를 상속 받았기 때문에 Derived
에서도 what() 호출 가능</li>
<li>만약 Derived에서 what을 동일하게 정의해 준다면
Derived what()이 Base what()을 오버라이딩 되어
Derived what()이 호출 되어 &#39;파생&#39;이 출력 됨</li>
</ul>
</li>
</ul>
<h2 id="protected">protected</h2>
<p>접근 지시자 protected</p>
<p>상속받는 클래스에서는 접근 가능하고 그 외의 기타 정보는 접근 불가능</p>
<ul>
<li>protected
  외부에서 접근 불가능. 단, 상속 받으면 가능</li>
<li>private 
  외부에서 접근 불가능. 자기 자신 class만 가능</li>
<li>public
  외부에서 접근 가능</li>
</ul>
<h3 id="class-상속-접근-지시자">class 상속 접근 지시자</h3>
<p><code>class A : public(접근 지시자) B</code></p>
<ul>
<li><p>public 상속
  기반 클래스의 접근 지시자들에 영향 없이 그대로 작동 </p>
</li>
<li><p>protected 상속
  public 은 protected로 바뀜 나머지는 그대로 유지</p>
</li>
<li><p>private 상속
  모든 접근 지시자들이 private으로 바뀜</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[연산자 오버로딩 & friend & C++ 스타일의 캐스팅]]></title>
            <link>https://velog.io/@choi_612/CPP3</link>
            <guid>https://velog.io/@choi_612/CPP3</guid>
            <pubDate>Fri, 27 May 2022 10:12:56 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>6장 연산자 오버로딩(overloading) 188p - 269p</p>
<hr>
<h2 id="대표적-연산자">대표적 연산자</h2>
<ul>
<li>+, -, * 와 같은 산술 연산자</li>
<li>+=, -= 와 같은 축약형 연산자</li>
<li>&gt;=, == 와 같은 비교 연산자</li>
<li>&amp;&amp;, || 와 같은 논리 연산자</li>
<li>-&gt; 나 * 와 같은 멤버 선택 연산자 (역참조 연산자)</li>
<li>++, -- 증감 연산자</li>
<li>[ ] 배열 연산자 와 () 함수 호출 연산자</li>
<li>= 대입 연산자 </li>
</ul>
<h2 id="연산자-오버로딩overloading">연산자 오버로딩(overloading)</h2>
<h3 id="연산자-오버로딩">연산자 오버로딩</h3>
<p>기본 연산자들을 직접 사용자가 정의 사용</p>
<ul>
<li><p>C++에서는 사용자 정의 연산자 사용 가능</p>
<ul>
<li>클래스 내부 or 전역 함수도 가능</li>
</ul>
<p>※이항 연산자 (피연산자를 두개 취하는 연산자) 주의 </p>
</li>
<li><p>기본적이 format
<code>(리턴 타입) operator(연산자) (연산자가 받는 인자)</code></p>
</li>
<li><p>example code</p>
<pre><code class="language-cpp">// == 
bool MyString::operator==(MyString&amp; str) {
  return !compare(str); // str 과 같으면 compare 에서 0 을 리턴한다.
}
// &lt;&lt; 
std::ostream&amp; operator&lt;&lt;(
    std::ostream&amp; os, const Complex&amp; c) { os &lt;&lt; &quot;( &quot; &lt;&lt; c.real &lt;&lt; &quot; , &quot; &lt;&lt; c.img &lt;&lt; &quot; ) &quot;; 
      return os;
}
// +
Complex operator+(const Complex&amp; a, const Complex&amp; b) {
  Complex temp(a.real + b.real, a.img + b.img);
  return temp;
}
// []
char&amp; MyString::operator[](const int index) { return string_content[index]; }
</code></pre>
</li>
<li><p>주요 포인트 <br>
  • 두 개의 동등한 객체 사이에서의 이항 연산자는 멤버 함수가 아닌 외부 함수로 오버로딩 하는 것이 좋음 
  • 두 개의 객체 사이의 이항 연산자 이지만 값의 변화가 동등하지 않는 이항 연산자는 멤버 함수로 오버로딩 하는 것이 좋음
  • 단항 연산자는 멤버 함수로 오버로딩 하는 것이 좋음
  • 일부 연산자들은 반드시 멤버 함수로만 오버로딩 해야함</p>
</li>
</ul>
<h3 id="타입-변환-연산자">타입 변환 연산자</h3>
<p>사용자 정의 객체를 다른 자료형의 변수에 대입 하고자 사용</p>
<ul>
<li>기본적인 format
<code>operator (변환 하고자 하는 타입) ()</code></li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
class Int {
    int data;
    // some other data
    public:
        Int(int data) : data(data) {}
        Int(const Int&amp; i) : data(i.data) {}
        operator int() { return data; }
};
int main() {
    Int x = 3;
    int a = x + 4;

    x = a * 2 + x + 4;
    std::cout &lt;&lt; x &lt;&lt; std::endl;
}</code></pre>
<h2 id="friend">friend</h2>
<p>클래스나 함수에서 friend로 선언된 다른 클래스의 private 및 protected 멤버에 접근</p>
<p>클래스 private 인 경우 접근 불허 그러나 필요의 경우 해당 클래스나 함수에서 접근 가능하기 위해 사용 </p>
<p>참고 : <a href="https://genesis8.tistory.com/98">https://genesis8.tistory.com/98</a></p>
<p><img src="https://velog.velcdn.com/images/choi_612/post/5fd3e9f1-819f-4625-881a-a7475390427d/image.png" alt=""></p>
<h3 id="friend-클래스">friend 클래스</h3>
<p>class 전체를 public으로 접근 가능
friend를 선언한 class에 해당 class에서 지정한 class가 접근가능</p>
<p>example code</p>
<pre><code class="language-cpp">class A{
public:
    A(){a=0;}
    friend B;
private:
    int a=0;
};

// class B에서 A의 private 접근 가능 
class B{
public:
    B(){b=0;}
    void print(A&amp; classa){
        cout&lt;&lt; classa.a&lt;&lt;endl;
        }
private:
    int b=0;
};</code></pre>
<h3 id="friend-맴버-함수">friend 맴버 함수</h3>
<p>class 전체가 아닌 class의 특정 멤버 함수만 friend로 선언 
friend를 선언한 class에 해당 class에서 지정한 class의 멤버 함수만 접근가능</p>
<pre><code class="language-cpp">class A{
public:
    A(){a=0;}
    friend void B::print(A% classa);
private:
    int a=0;
};

// class B에서 A의 private 접근 가능 
class B{
public:
    B(){b=0;}
    void print(A&amp; classa){
        cout&lt;&lt; classa.a&lt;&lt;endl;
        }
private:
    int b=0;
};</code></pre>
<h3 id="friend-전역-함수">friend 전역 함수</h3>
<p>접근 지정자를 무시하고 클래스 내부의 멤버에 접근</p>
<pre><code class="language-cpp">
class B{
public:
    B(){b=0;}
private:
    int b=0;
    friend void print(B&amp; classb){
        cout&lt;&lt; classb.b&lt;&lt;endl;
        }
};

//class내 private 내부에 있지만 friend를 사용하므로써 외부에서 해당 멤버 함수 사용가능 

int main(){
    B bb;
    bb.print();
}</code></pre>
<h2 id="c-스타일의-캐스팅">C++ 스타일의 캐스팅</h2>
<h3 id="4가지-종류">4가지 종류</h3>
<ul>
<li>static_cast
  우리가 흔히 생각하는, 언어적 차원에서 지원하는 일반적인 타입 변환</li>
<li>const_cast
  객체의 상수성(const) 를 없애는 타입 변환. 쉽게 말해 const int 가 int로 바뀜</li>
<li>dynamic_cast 
  파생 클래스 사이에서의 다운 캐스팅</li>
<li>reinterpret_cast
  위험을 감수하고 하는 캐스팅으로 서로 관련이 없는 포인터들 사이의 캐스팅</li>
</ul>
<p>※ 생각보다 우리 코드에서 자주 보인다 알아두자</p>
<h3 id="기본적인-format">기본적인 format</h3>
<p><code>(원하는 캐스팅 종류)&lt;바꾸려는 타입&gt;(무엇을 바꿀 것인가?)</code></p>
<p>example code</p>
<pre><code class="language-cpp">//static_cast 로 float 타입의 float_variable -&gt; int 타입 변환
static_cast&lt;int&gt;(float_variable);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 객체 지향 프로그래밍(oop)]]></title>
            <link>https://velog.io/@choi_612/CPP2</link>
            <guid>https://velog.io/@choi_612/CPP2</guid>
            <pubDate>Wed, 18 May 2022 08:20:51 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>5장 객체지향 프로그래밍(oop) 57p - 187p</p>
<hr>
<h1 id="객체-지향-언어">객체 지향 언어</h1>
<p>object oriented language</p>
<ul>
<li><p>추상화 ( Abstraction ) <br>
=&gt; 컴퓨터에서 처리 할 수 있도록 현실 세계를 반영하여 함수와 변수화 </p>
</li>
<li><p>캡슐화 ( Encapsulation)<br>
=&gt; 변수들을 외부로 보호 
=&gt; 객체의 인스턴스 변수의 값은 인스턴수 함수를 통해서 변경 ( 간접적으로 조절 )<br>
* 사용 이유<br> 
=&gt; 객채 내부적인 처리는 알 필요 없도록 함
=&gt; 복잡한 객채의 일들을 내부에서 처리 </p>
</li>
</ul>
<h2 id="객체">객체</h2>
<p><img src="https://velog.velcdn.com/images/choi_612/post/df0b8372-a072-4d0d-96d9-f9b2d3ab1051/image.png" alt="object"></p>
<h3 id="object">object</h3>
<p>variale + method 
=&gt; 변수 + 함수 </p>
<p>객체에 정의 되면 
=&gt; instance variable + instance method 
변수들과 참고 자료들로 이루어진 소프트웨어 덩이리</p>
<h2 id="클래스">클래스</h2>
<p><img src="https://velog.velcdn.com/images/choi_612/post/07bf263d-7cc3-402f-9331-fa956acdd610/image.png" alt="class"></p>
<h3 id="class">class</h3>
<p>객체를 만들기 위한 뼈대 이자 설계도 </p>
<p>클래스
=&gt; member variale + member method </p>
<p>c++에서 클래스를 이용해 만들어진 메모리에 객체 =&gt; instance</p>
<p>Example code </p>
<pre><code class="language-cpp">include &lt;iostream&gt;

class Animal {
  private:
    int food;
    int weight;
  public:
    void set_animal(int _food, int _weight) {
    food = _food;
    weight = _weight;
    }
    void increase_food(int inc) {
    food += inc;
    weight += (inc / 3);
    }
    void view_stat() {
    std::cout &lt;&lt; &quot;이 동물의 food : &quot; &lt;&lt; food &lt;&lt;     std::endl;
    std::cout &lt;&lt; &quot;이 동물의 weight : &quot; &lt;&lt; weight &lt;&lt;     std::endl;
    }
}; // 세미콜론 잊지 말자!

int main() {
    Animal animal;
    animal.set_animal(100, 50);
    animal.increase_food(30);
    animal.view_stat();
    return 0;
}
</code></pre>
<h3 id="클래스-인스턴스-생성">클래스 인스턴스 생성</h3>
<p>클래스 이름으로 명시</p>
<h3 id="class-접근-지시자">class 접근 지시자</h3>
<p>외부에서의 접근 
가능 public
불가능 private &lt; 객체내 보호를 위해 내부에서만 접근 가능
* protected &lt; 상속인 경우에만 접근 가능 <br>
키워드 명시 없다면 private으로 설정 </p>
<h3 id="맴버-함수">맴버 함수</h3>
<p>해당 class 내 멤버 변수는 명시 없시 사용 가능 </p>
<h2 id="함수의-오버로딩">함수의 오버로딩</h2>
<h3 id="overloading">Overloading</h3>
<p>함수의 이름이 같더라도 인자가 다르면 다른 함수라고 판단 </p>
<p>example code </p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void print(int x) { std::cout &lt;&lt; &quot;int : &quot; &lt;&lt; x &lt;&lt; std::endl; }
void print(double x) { std::cout &lt;&lt; &quot;double : &quot; &lt;&lt; x &lt;&lt; std::endl; }

int main() {
    int a = 1;
    char b = &#39;c&#39;;
    double c = 3.2f;

    print(a);
    print(b);
    print(c);

    return 0;</code></pre>
<blockquote>
<p>int : 1
int : 99 // char가 아니라 int
double : 3.2</p>
</blockquote>
<h4 id="c-컴파일러-처리">c++ 컴파일러 처리</h4>
<ol>
<li>자신과 타입이 정확히 일치 함수 찾기</li>
<li>정확히 일치하는 타입이 없는 경우 비슷한 형변환을 통해 일치하는 함수 찾기</li>
<li>없다면 더 포괄적인 형변환을 통해 일치하는 함수 찾기</li>
<li>유저 정의된 타입 변환으로 일치하는 것 찾기</li>
</ol>
<p>if)  그래도 없다면 or 두 개 이상 같으면 error: ambiguous</p>
<h2 id="생성자-및-소멸자">생성자 및 소멸자</h2>
<h3 id="constructor">constructor</h3>
<p>객체 생성시 자동으로 호출되는 함수 &gt; 객체를 초기화 해주는 역할 </p>
<pre><code class="language-cpp">// 객체를 초기화 하는 역할을 하기 때문에 리턴값이 없다!
/* 클래스 이름 */ (/* 인자 */) {}</code></pre>
<p>인자에 맞춰 생성자를 호출하여 객체를 생성</p>
<p>example code</p>
<pre><code class="language-cpp">//생성자
Date(int year, int month, int day)

//객체 생성
Date day(2011, 3, 1); // 암시적 방법 (implicit)
Date day = Date(2012, 3, 1); // 명시적 방법 (explicit)
</code></pre>
<h3 id="default-constructor">default constructor</h3>
<h4 id="디폴트-생성자">디폴트 생성자</h4>
<p>생성자 정의를 하지 않아도 컴파일러가 자동으로 추가하여 디폴트 생성자를 호출 
인자를 하나도 가지지 않는 생성자 </p>
<p>example code </p>
<pre><code class="language-cpp">//디폴트 생성자 정의
Date() {
    year = 2012;
    month = 7;
    day = 12;
}
//객체 선언
Date day = Date();
Date day2;
//객제가 아니라 하나의 함수로 인식 됨 *주의*
Date day3(); 

//명시적으로 디폴트 생성자 사용 
class Test {
  public:
    Test() = default; // 디폴트 생성자를 정의해라
};</code></pre>
<h3 id="생성자-오버로딩">생성자 오버로딩</h3>
<p>디폴트 생성자 제외하고 다른 인자들을 갖는 생성자 정의하여 생성자 오버로딩 
클래스의 객체를 여러가지 인자타입으로 초기화 하여 객체 생성 가능 </p>
<h3 id="소멸자">소멸자</h3>
<h4 id="destructor">Destructor</h4>
<p>객체가 소멸 될 때 자동으로 호출 되는 함수 
메모리 누수 방지로 동적 할당 delete 할 수 있도록 한다.</p>
<ul>
<li><p>default destructor
소멸자가 필요 없다면 구지 적지 않아도 컴파일러가 생성 </p>
</li>
<li><p>메모리 누수
객체내 동적할당 시 객체가 delete된 후 지워질 수 없어 
메모리 공간에 쌓이며 필요없는 메모리 점유를 하게 된다. </p>
</li>
<li><p>소멸자가 불리는 순서 주의 
전역 객체는 전체 프로그램 종료할 때 호출 
지역 객체는 해당 지역이 없어질 때 호출</p>
</li>
</ul>
<p>example code </p>
<pre><code class="language-cpp">// 소멸자
~(클래스의 이름)

Marine::~Marine() {
    std::cout &lt;&lt; name &lt;&lt; &quot; 의 소멸자 호출 ! &quot; &lt;&lt;         std::endl;
    if (name != NULL) {
    delete[] name;
    }
}</code></pre>
<h3 id="복사-생성자">복사 생성자</h3>
<h4 id="copy-constructor">copy constructor</h4>
<p>class T 의 객체 a 를 상수 레퍼런스로 받아 복사 하여 객체 생성 
const이므로 내부에서 a 데이터 변경은 불가능
새로운 인스턴스 변수 초기화시 데이터 복사하여 사용 </p>
<ul>
<li><p>Default copy constructor
c++ 컴파일러에서 지원 
대응 되는 원소를 1 대 1로 복사</p>
<ul>
<li>깊은 복사 /얕은 복사
깊은 복사
메모리를 새로 할당해서 내용 복사
얕은 복사
단순히 대입만 해서 복사</li>
<li>깊은 복사를 원할 때는 사용자가 직접 복사 생성자 정의 </li>
</ul>
</li>
</ul>
<p>example code</p>
<pre><code class="language-cpp">// class T에서 복사 생성자
// T(const T&amp; a);

Photon_Cannon::Photon_Cannon(const Photon_Cannon&amp; pc) {
    std::cout &lt;&lt; &quot;복사 생성자 호출 !&quot; &lt;&lt; std::endl;
    hp = pc.hp;
    shield = pc.shield;
    coord_x = pc.coord_x;
    coord_y = pc.coord_y;
    damage = pc.damage;

    //깊은 복사
    name = new char[strlen(pc.name) + 1];
    strcpy(name, pc.name);
}

//복사 생성자를 이용한 객체 생성 
Photon_Cannon pc1(3, 3);
Photon_Cannon pc2(pc1);
// &#39;=&#39; 이용한 복사 생성자 호출
Photon_Cannon pc3 = pc2;
</code></pre>
<h3 id="생성자-초기화-리스트">생성자 초기화 리스트</h3>
<h4 id="initializer-list">initializer list</h4>
<p>생성자를 호출하는 동시에 멤버 변수를 초기화</p>
<ul>
<li><p>초기화 리스 사용 안 할 시
생성 먼저한 후 대입 수행 &lt; 초기화 리스트가 효율적</p>
</li>
<li><p>const
멤버 변수 정의를 const하여 초기화 후 변경 불가능 </p>
</li>
</ul>
<p>example code </p>
<pre><code class="language-cpp">// 초기화 리스트
// (생성자 이름) : var1(arg1), var2(arg2) {}

Marine::Marine(int coord_x, int coord_y)
: coord_x(coord_x), coord_y(coord_y), hp(50), damage(5), is_dead(false) {}
</code></pre>
<h2 id="static">static</h2>
<h3 id="static-변수">static 변수</h3>
<p>클래스 하나에만 종속되는 변수
어떤 클래스의 static 멤버 변수는 프로그램이 종료 될때 소멸 
클래스의 모든 객체들이 공유 하는 하나의 변수</p>
<ul>
<li>static 변수 초기화
클래스 내부에서는 불가능
클래스 외부에서 초기화 가능 
const static 변수일때만 내부에서 초기화 가능 </li>
<li>static 변수 관리
주로 생성자와 소멸자에 추가해서 사용<pre><code class="language-cpp">int Marine::total_marine_num = 0;</code></pre>
</li>
</ul>
<h3 id="static-함수">static 함수</h3>
<p>객체가 없어도 그냥 클래스 자체에서 호출
클래스 전체에 딱 1 개 존재하는 함수
내부에 그냥 클래스의 멤버 변수들을 이용 불가능&lt; 객체를 지정하지 않으므로</p>
<p>example code</p>
<pre><code class="language-cpp">// class 내 함수 선언
static void show_total_marine();

//함수 정의
void Marine::show_total_marine() {
    std::cout &lt;&lt; &quot;전체 마린 수 : &quot; &lt;&lt; total_marine_num &lt;&lt; std::endl;
}
//함수 사용시 class&lt;T&gt;:: (static 함수)형식으로 사용
Marine::show_total_marine();</code></pre>
<h2 id="pointer--reference-in-class">pointer &amp; reference in class</h2>
<h3 id="this">this</h3>
<p>호출하는 객체 자신을 가리키는 포인터
static 함수를 제외한 모든 멤버 함수에 정의 되어 있다</p>
<p>example code</p>
<pre><code class="language-cpp">Marine&amp; Marine::be_attacked(int damage_earn) {
    hp -= damage_earn;
    if (hp &lt;= 0) is_dead = true;
    return *this;
}</code></pre>
<h3 id="레퍼런스를-리턴하는-함수">레퍼런스를 리턴하는 함수</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[c++ 참조자 레퍼런스]]></title>
            <link>https://velog.io/@choi_612/CPP1</link>
            <guid>https://velog.io/@choi_612/CPP1</guid>
            <pubDate>Tue, 17 May 2022 09:15:13 GMT</pubDate>
            <description><![CDATA[<p>*<em>씹어먹는 C++ *</em></p>
<p>3장 c++ 참조자 래퍼런스 30p ~ 46p</p>
<hr>
<h1 id="pointer--reference">Pointer &amp; reference</h1>
<h2 id="difference-between-pointer-and-reference">Difference between pointer and reference</h2>
<br>

<table>
<thead>
<tr>
<th>diff</th>
<th>pointer</th>
<th>reference</th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>사용법</td>
<td><code>*</code>( 변수이름 )</td>
<td>( 타입 )&amp;</td>
</tr>
<tr>
<td></td>
<td>or ( 타입 )<code>*</code></td>
<td>or &amp;( 변수이름 )</td>
</tr>
<tr>
<td>개념</td>
<td>주소값 저장</td>
<td>별명으로 해당 주소값 사용</td>
</tr>
<tr>
<td>사용 이유</td>
<td></td>
<td>불필요한 &amp;, <code>*</code> 사용이 줄어듦</td>
</tr>
<tr>
<td>정의 시</td>
<td>변수 명시 불필요</td>
<td>반드시 변수 명시</td>
</tr>
<tr>
<td>참조 변경</td>
<td>다른 주소값으로 가능</td>
<td>변경 불가능</td>
</tr>
<tr>
<td>메모리 할당</td>
<td>공간 할당</td>
<td>컴파일러 구현에 따라 다름</td>
</tr>
<tr>
<td>배열 생성</td>
<td>가능</td>
<td>불가능</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><br></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h1 id="reference">reference</h1>
<h3 id="함수-인자로-받기">함수 인자로 받기</h3>
<p>실제 변수를 넘기는 것과 같다</p>
<pre><code class="language-cpp">int change_val(int &amp;p) {
p = 3;
return 0;
}

int main() {
int number = 5;
std::cout &lt;&lt; number &lt;&lt; std::endl;
change_val(number);
std::cout &lt;&lt; number &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>result
5
3</p>
</blockquote>
<h3 id="상수-참조-불가능">상수 참조 불가능</h3>
<p>원한다면  const 사용</p>
<pre><code class="language-cpp">// do not use 
int &amp;ref = 4;

// use
const int &amp;ref = 4;
</code></pre>
<h3 id="레퍼런스-배열-불가능">레퍼런스 배열 불가능</h3>
<p>메모리 상 존재하지 않을 수 있으므로 <br>
<strong>하지만</strong> 배열의 레퍼런스 생성은 가능 
포인터와 다르게 반드시 크기 명시 </p>
<pre><code class="language-cpp">int arr[3] = {1, 2, 3};
int(&amp;ref)[3] = arr; // refenence 크기 반드시 명시
ref[0] = 2;
ref[1] = 3;
ref[2] = 1;</code></pre>
<h3 id="레퍼러스-리턴-주의-dangling-reference">레퍼러스 리턴 (※주의※ Dangling reference)</h3>
<p>함수 사용시 지역변수 레퍼런스를 리턴하게 되면 함수 종료 후 사라지기 때문에 reference가 참조 할 것이 사라지게 된다.<br>
<strong>오류를 피하기 위해 이왕이면</strong> 
외부 변수 레퍼런스로 리턴 하자 </p>
<p>만약 다른 타입으로 return 하고 레퍼런스로 받는 다면 컴파일 오류가 발생한다. 
const를 사용하면 가능 하나 사라지기 전까지만 사용 가능 하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CPP 스터디]]></title>
            <link>https://velog.io/@choi_612/CPP0</link>
            <guid>https://velog.io/@choi_612/CPP0</guid>
            <pubDate>Tue, 10 May 2022 09:21:36 GMT</pubDate>
            <description><![CDATA[<p align = "center">
    <img src="https://velog.velcdn.com/images/choi_612/post/7943f865-9c17-469e-98c7-70313954cdf4/image.png" width="1%" height="1%">
<p/>


<p><strong>참고 교재</strong></p>
<p><a href="https://modoocode.com/312">&quot;씹어 먹는 C++&quot;</a> </p>
<hr>
<p>Start : 22.05.16
End : 22.07.12</p>
<p>Every Tuesday 7pm.</p>
<p>Study with <a href="https://velog.io/@tera_geniel">tera_geniel</a>, <a href="https://velog.io/@pesu1027">pesu1027</a>, <a href="https://velog.io/@j2013yk">j2013yk</a></p>
<hr>
<h4 id="주차별-스터디-내용">주차별 스터디 내용</h4>
<p><strong>0주차</strong>
스터디 규칙 상세 결정 및 컨펌, 피드백</p>
<p><strong>1주차</strong>
3장 c++ 참조자 래퍼런스 30p ~ 46p
5장 객체지향 프로그래밍(oop) 57p - 187p</p>
<p><strong>2주차</strong>
6장 연산자 오버로딩(overloading) 188p - 269p
7장 클래스의 상속 270p - 297p</p>
<p><strong>3주차</strong>
7장 클래스의 상속 298p - 339p
10장 c++ 템플릿 410p-493p</p>
<p><strong>4주차</strong>
11장 c++ 표준 라이브러리(stl) 494p-555p
11장-3, 11장-4 556p - 612p</p>
<p><strong>5주차</strong>
12장 c++에서의 예외 처리 613p-629p
13장 우측값과 이동 연산 630p-648p</p>
<p><strong>6주차</strong>
13장 우측값과 이동 연산 649p-670p
14장 스마트 포인터 671p-704p
15장 함수 객체 705p-718p</p>
<p><strong>7주차</strong>
16장 C++ 쓰레드 719p-825p</p>
<p><strong>8주차</strong>
실제로 코드 짜보며 공부하기</p>
]]></description>
        </item>
    </channel>
</rss>