<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>초</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 17 Aug 2025 13:49:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>초</title>
            <url>https://velog.velcdn.com/images/chowon_/profile/e5a9fd52-c433-4fcf-847e-2971570c46f5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 초. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/chowon_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[혼공컴운] Chapter 01 ~ 03]]></title>
            <link>https://velog.io/@chowon_/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-Chapter-01-03</link>
            <guid>https://velog.io/@chowon_/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-Chapter-01-03</guid>
            <pubDate>Sun, 17 Aug 2025 13:49:32 GMT</pubDate>
            <description><![CDATA[<h3 id="chapter-01--컴퓨터-구조-시작하기">Chapter 01 | 컴퓨터 구조 시작하기</h3>
<ul>
<li><p>컴퓨터 구조를 알아야 하는 이유</p>
<ul>
<li>컴퓨터 구조를 이해하면 문제 해결 능력이 향상됨</li>
<li>성능, 용량, 비용을 고려하며 개발할 수 있음</li>
</ul>
</li>
<li><p>컴퓨터 구조의 큰 그림</p>
<ul>
<li><p>우리가 알아야 하는 컴퓨터 구조 지식 : 컴퓨터가 이해하는 정보 + 컴퓨터의 네 가지 핵심 부품</p>
</li>
<li><p>컴퓨터가 이해하는 정보 : 0과 1로 표현된 정보</p>
<ul>
<li>데이터 : 컴퓨터가 이해하는 숫자, 문자, 이미지, 동영상과 같은 정적인 정보</li>
<li>명령어 : 데이터를 움직이고 컴퓨터를 작동시키는 정보</li>
</ul>
</li>
<li><p>컴퓨터의 네 가지 핵심 부품</p>
<p>  시스템 버스 : 컴퓨터의 네 가지 핵심 부품들이 서로 정보를 주고받는 통로</p>
<ul>
<li>중앙처리장치(CPU) : 메모리에 저장된 명령어를 읽어 들이고, 해석하고, 실행</li>
<li>주기억장치(메모리) : 현재 실행되는 프로그램의 명령어와 데이터를 저장</li>
<li>보조기억장치 : 전원이 꺼져도 보관할 프로그램을 저장</li>
<li>입출력장치 : 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환<h3 id="chapter-02--데이터">Chapter 02 | 데이터</h3>
</li>
</ul>
</li>
</ul>
</li>
<li><p>0과 1로 숫자를 표현하는 방법</p>
<ul>
<li>비트 : 0과 1로 표현할 수 있는 가장 작은 정보 단위</li>
<li>word : CPU가 한 번에 처리할 수 있는 데이터 크기</li>
<li>이진법 : 1을 넘어가는 시점에 자리 올림을 하여 0과 1만으로 수를 표현하는 방법<ul>
<li>음수 표현 시 플래그와 2의 보수(모든 0과 1을 뒤집고 + 1한 값) 사용</li>
<li>이진법으로 모든 숫자를 표현하면 숫자의 길이가 길어지므로 십육진법도 자주 사용함<ul>
<li>십육진수를 이진수로 변환 : 한 글자를 4비트의 이진수로 간주</li>
<li>이진수를 십육진수로 변환 : 이진수 숫자를 네 개씩 끊고 십육진수로 변환</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>0과 1로 문자를 표현하는 방법</p>
<ul>
<li>문자 집합 : 컴퓨터가 인식하고 표현할 수 있는 문자의 모음</li>
<li>문자 인코딩 : 문자를 0과 1로 변환하는 과정 (↔ 문자 디코딩)</li>
<li>코드 포인트 : 글자에 부여된 고유한 값 (아스키 문자 A의 코드 포인트는 65)</li>
<li>아스키 코드 : 아스키 문자 집합에 0 ~  127까지의 수가 할당되어 인코딩 된 문자 표현 방식</li>
<li>한글 인코딩 방식<ul>
<li>완성형 인코딩 : 초성, 중성, 종성의 조합으로 이루어진 완성된 하나의 글자에 고유한 코드 부여하는 방식</li>
<li>조합형 인코딩 : 초성을 위한 비트열, 중성을 위한 비트열, 종성을 위한 비트열을 할당하여 그것들의 조합으로 하나의 글자 코드를 완성하는 방식</li>
</ul>
</li>
<li>EUC-KR : 완성형 인코딩 방식으로 2350개 정도의 한글 단어 표현 가능 (표현 불가능한 단어가 있음)</li>
<li>CP949 : EUC-KR의 확장 버전 (표현 불가능한 단어가 있음)</li>
<li>유니코드 : 대부분 나라의 문자, 특수문자, 화살표, 이모티콘 등을 표현 가능<ul>
<li>UTF(Unicode Transformation Format) : 유니코드의 글자를 인코딩하는 방법 (예) UTF-8, UTF-16, UTF-32 등</li>
<li>UTF-8 : 통상 1바이트 ~ 4바이트까지의 인코딩 결과를 만들어 냄<ul>
<li>한 : D55C₍₁₆₎, 글 : AE00₍₁₆₎ 이므로 두 글자 모두 0800₍₁₆₎ ~FFFF₍₁₆₎ 사이에 있음 ⇒ 3바이트</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="chapter-03--명령어">Chapter 03 | 명령어</h3>
<ul>
<li>소스 코드와 명령어<ul>
<li>고급 언어 : 사람이 이해하고 작성하기 쉽게 만들어진 언어</li>
<li>저급 언어 : 컴퓨터가 직접 이해하고 실행할 수 있는 언어<ul>
<li>기계어 : 0과 1의 명령어 비트로 이루어진 언어</li>
<li>어셈블리어 : 기계어를 읽기 편한 형태로 번역한 언어</li>
</ul>
</li>
<li>고급 언어 → 저급 언어로 변환하는 방식<ul>
<li>컴파일 방식 : 컴파일러에 의해 전체 코드를 저급 언어(목적 코드)로 변환하는 방식으로 소스 코드 컴파일 중 오류가 발생하면 소스 코드 전체가 실행되지 않음</li>
<li>인터프리트 방식 : 소스 코드를 한 줄씩 차례로 실행하는 방식으로 오류가 발생하면 오류 발생 전까지의 코드는 실행됨</li>
</ul>
</li>
<li>목적 파일 vs 실행 파일<ul>
<li>목적 파일 : 목적 코드로 이루어진 파일<ul>
<li>목적 코드가 실행 코드가 되기 위해서는 링킹 과정이 필요함</li>
</ul>
</li>
<li>실행 파일 : 실행 코드로 이루어진 파일</li>
</ul>
</li>
</ul>
</li>
<li>명령어의 구조<ul>
<li>명령어 : 연산 코드와 오퍼랜드로 구성되어 있음<ul>
<li>연산 코드(연산자) : 명령어가 수행할 연산(데이터 전송, 산술/논리 연산, 제어 흐름 변경, 입출력 제어)</li>
<li>오퍼랜드(피연산자) : 연산에 사용할 데이터가 저장된 위치</li>
</ul>
</li>
<li>주소 지정 방식 : 연산에 사용할 데이터 위치를 찾는 방법<ul>
<li>즉시 주소 지정 방식 : 오퍼랜드 필드에 연산에 사용할 데이터를 직접 명시하는 방법</li>
<li>직접 주소 지정 방식 : 오퍼랜드 필드에 유효 주소(메모리 주소)를 직접적으로 명시하는 방식</li>
<li>간접 주소 지정 방식 : 오퍼랜드 필드에 유효 주소의 주소를 명시하는 방식</li>
<li>레지스터 주소 지정 방식 : 오퍼랜드 필드에 연산에 사용할 데이터를 저장한 레지스터를 직접 명시하는 방법</li>
<li>레지스터 간접 주소 방식 : 연산에 사용할 데이터를 메모리에 저장하고, 그 주소를 지정한 레지스터를 오퍼랜드 필드에 명시하는 방법</li>
</ul>
</li>
</ul>
</li>
<li>스택과 큐<ul>
<li>스택 : 한쪽 끝이 막혀 있는 통과 같은 저장 공간으로 LIFO(Last In First Out) 방식으로 데이터를 관리</li>
<li>큐 : 양쪽이 뚫려 있는 통과 같은 저장 공간으로 FIFO(First In First Out) 방식으로 데이터를 관리</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자, 소멸자 및 대입 연산자]]></title>
            <link>https://velog.io/@chowon_/%EC%83%9D%EC%84%B1%EC%9E%90-%EC%86%8C%EB%A9%B8%EC%9E%90-%EB%B0%8F-%EB%8C%80%EC%9E%85-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@chowon_/%EC%83%9D%EC%84%B1%EC%9E%90-%EC%86%8C%EB%A9%B8%EC%9E%90-%EB%B0%8F-%EB%8C%80%EC%9E%85-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Thu, 19 Dec 2024 07:43:35 GMT</pubDate>
            <description><![CDATA[<h3 id="c가-은근슬쩍-만들어-호출해-버리는-함수들에-촉각을-세우자">C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자</h3>
<h4 id="컴파일러가-선언해-주는-함수">컴파일러가 선언해 주는 함수</h4>
<ul>
<li>기본 생성자</li>
<li>소멸자</li>
<li>복사 생성자</li>
<li>복사 대입 연산자</li>
</ul>
<pre><code class="language-cpp">class Empty
{
public:
    // 기본 생성자
    Empty() { ... } // 생성자를 하나라도 만들면 컴파일러는 기본 생성자를 선언하지 않음
    // 소멸자
    ~Empty() { ... }
    // 복사 생성자
    Empty(const Empty&amp; rhs) { ... }
    // 복사 대입 연산자
    Empty&amp; operator=(const Empty&amp; rhs) { ... }
};</code></pre>
<p>참조자를 데이터 멤버로 갖고 있는 클래스에 대입 연산을 지원하기 위해서는 직접 복사 대입 연산자를 정의해 주어야 함</p>
<pre><code class="language-cpp">template&lt;class T&gt;
class NamedObject
{
public:
    NamedObject(std::string&amp; name, cosnt T&amp; value);

private:
    std::string&amp; nameValue;
    const T objectValue;
};

int main()
{
    std::string newDog(&quot;Persephone&quot;);
    std::string oldDog(&quot;Satch&quot;);

    NamedObject&lt;int&gt; p(newDog, 2);
    NamedObject&lt;int&gt; s(oldDog, 36);

    p = s; // 컴파일 에러
}</code></pre>
<p>데이터 멤버가 상수 객체인 경우에도 C++ 컴파일러가 비슷하게 동작함
상수 멤버를 수정하는 것은 문법에 어긋나기 때문에 자동으로 만들어진 복사 대입 연산자 내부에서는 상수 멤버를 어떻게 처리해야할지 모호해짐</p>
<h3 id="컴파일러가-만들어낸-함수가-필요-없으면-확실히-이들의-사용을-금해-버리자">컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자</h3>
<p>컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면 대응되는 멤버 함수를 <code>private</code>으로 선언한 후에 구현은 하지 않은 채로 두면 됨
하지만 이 경우에는 <code>friend</code>나 <code>멤버 함수</code>를 통해서는 접근이 가능하기 때문에 <code>delete</code>를 사용하여 함수를 삭제하는 것이 좋음</p>
<h3 id="다형성을-가진-기본-클래스에서는-소멸자를-반드시-가상-소멸자로-선언하자">다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자</h3>
<p>소멸자는 파생 클래스 -&gt; 기본 클래스 순으로 호출됨
기본 클래스를 사용하여 파생 클래스의 메모리를 해제하는 경우 <code>virtual</code> 키워드를 사용해야만 파생 클래스의 소멸자부터 호출됨
이렇게 하지 않으면 파생 클래스의 힙 영역에 저장된 메모리가 있는 경우 제대로 해제가 되지 않아 누수가 발생될 수 있음</p>
<p>가상 함수를 C++에서 구현하려면 클래스에 별도의 자료구조(<code>vptr</code>)가 하나 들어가야 함
<code>vptr</code>은 가상 함수의 주소, 즉 포인터들의 배열을 가리키고 있으며 가상 함수 테이블 포인터의 배열은 <code>vtbl</code>이라고 불림</p>
<p><code>vptr</code>이 추가 되면 객체의 크기가 커지기 때문에 모든 객체의 소멸자를 <code>virtual</code>로 선언해서는 안 됨
가상 소멸자를 선언하는 것은 그 클래스에 가상 함수가 하나라도 들어 있는 경우로 한정해야 함</p>
<p>경우에 따라 순수 가상 소멸자를 두면 편리하게 사용할 수 있음
순수 가상 함수는 해당 클래스를 추상 클래스로 만드는데 마땅히 넣을 만한 순수 가상 함수가 없는 경우가 종종 생김
이럴 때 순수 가상 소멸자를 선언하면 됨</p>
<h3 id="예외가-소멸자를-떠나지-못하도록-붙들어-놓자">예외가 소멸자를 떠나지 못하도록 붙들어 놓자</h3>
<p>소멸자에서는 예외가 빠져나가면 안 됨
만약 소멸자 안에서 호출되 함수가 예외를 던질 가능성이 있다면 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리던지 프로그램을 끝내든지 해야 함
어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(즉, 소멸자가 아닌 함수)이어야 함</p>
<h3 id="객체-생성-및-소멸-과정-중에는-절대로-가상-함수를-호출하지-말자">객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자</h3>
<p>기본 클래스의 생성자가 호출될 동안에는 가상 함수는 절대로 파생 클래스 쪽으로 내려가지 않음
그 대신 객체 자신이 기본 클래스 타입인 것처럼 동작함
기본 클래스 생성자가 돌아가고 있을 시점에 파생 클래스 데이터 멤버는 아직 초기화된 상태가 아님
어쩌다 호출된 가상 함수가 파생 클래스 쪽으로 내려간다면 파생 클래스 버전의 가상 함수는 초기화 되지 않은 데이터 멤버를 건드리게 되므로 미정의 동작이 이루어짐</p>
<h3 id="대입-연산자는-this의-참조자를-반환하게-하자">대입 연산자는 *this의 참조자를 반환하게 하자</h3>
<pre><code class="language-cpp">int x, y, z;
x = y = z = 15;</code></pre>
<p>대입이 사슬처럼 이루어지는 이유는 대입 연산자가 좌변 인자에 대한 참조자를 반환하도록 구현되어 있기 때문임
단순히 대입형 연산자 말고도 모든 형태의 대입 연산자에서 지켜져야 함
관례이기 때문에 지키지 않는다고 컴파일이 안 된다거나 하지는 않지만 모든 기본제공 타입들이 따르고 있을 뿐만 아니라 표준 라이브러리에 속한 모든 타입에서도 따르고 있으므로 지키는 것이 좋음</p>
<h3 id="operator에서는-자기대입에-대한-처리가-빠지지-않도록-하자">operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자</h3>
<h4 id="자기-대입">자기 대입</h4>
<p>어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것
같은 타입으로 만들어진 객체 여러 개를 참조자 혹은 포인터로 물어 놓고 동작하는 코드를 작성할 때는 같은 객체가 사용될 가능성을 고려하는 것이 일반적으로 바람직한 자세임</p>
<pre><code class="language-cpp">Widget&amp; Widget::operator=(const Widget&amp; rhs)
{
    Bitmap&amp; pOrig = pb;
    pb = new Bitmap(*rhs.pb);
    delete pOrig;

    return *this;
}</code></pre>
<p>이렇게 코드를 짜면 <code>new Bitmap</code> 부분에서 예외가 발생하더라도 <code>pb</code>는 변경되지 않은 상태로 유지되기 때문에 안전함
또한 일치성 검사 같은 것이 없음에도 불구하고 이 코드는 자기대입 현상을 완벽히 처리하고 있음</p>
<h3 id="객체의-모든-부분을-빠짐없이-복사하자">객체의 모든 부분을 빠짐없이 복사하자</h3>
<p>객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠뜨리지 말고 복사해야 함
클래스의 복사 함수 두 개를 구현할 때, 한쪽을 이용해서 다른 쪽을 구현하려는 시도는 하지 말고 공통된 동작을 제3의 함수에다 분리해 놓고 양쪽에서 이것을 호출하게 만들어서 해결해야 함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[예외 명세]]></title>
            <link>https://velog.io/@chowon_/%EC%98%88%EC%99%B8-%EB%AA%85%EC%84%B8</link>
            <guid>https://velog.io/@chowon_/%EC%98%88%EC%99%B8-%EB%AA%85%EC%84%B8</guid>
            <pubDate>Wed, 18 Dec 2024 02:17:21 GMT</pubDate>
            <description><![CDATA[<h3 id="예외를-방출하지-않을-함수는-noexcept로-선언하라">예외를 방출하지 않을 함수는 noexcept로 선언하라</h3>
<h4 id="noexcept">noexcept</h4>
<ul>
<li>해당 함수가 예외를 방출하지 않을 것임을 명시하는 키워드</li>
<li>해당 키워드를 사용하면 컴파일러가 더 나은 목적 코드를 산출할 수 있음</li>
</ul>
<pre><code class="language-cpp">int f(int x) throw();      // C++98 방식
int f(int x) noexcept(); // C++11 방식</code></pre>
<p>C++98에서는 예외 명세가 위반되면 호출 스택이 f를 호출한 지점에 도달할 때까지 풀리며 그 지점에서 몇 가지 동작이 취해진 후 프로그램 실행이 종료됨
C++11에서는 프로그램 실행이 종료되기 전에 호출 스택이 풀릴 수도 있고 풀리지 않을 수도 있음</p>
<p>호출 스택이 풀리는 것과 풀릴 수도 있는 것의 차이는 컴파일러의 코드 작성에 영향을 미침</p>
<p><code>noexcept</code> 함수에서 컴파일러의 옵티마이저는 예외가 함수 바깥으로 전파될 수 있다고 해도 실행지점 스택을 풀기 가능 상태로 유지할 필요가 없음
또한 예외가 <code>noexcept</code> 함수를 벗어난다고 해도 <code>noexcept</code> 함수 안의 객체들을 반드시 생성의 반대 순서로 파괴해야 하는 것도 아님
그러나 예외 명세가 <code>throw()</code>인 함수에는 그러한 최적화 유연성이 없으며, 예외 명세가 아예 없는 함수 역시 마찬가지로 그런 유연성이 없음</p>
<h4 id="예외를-방출하지-않으면-다-noexcept-키워드를-붙이면-되는거-아닌가">예외를 방출하지 않으면 다 noexcept 키워드를 붙이면 되는거 아닌가?</h4>
<p>대부분의 함수는 예외에 중립적임
예외 중립적 함수는 스스로 예외를 던지지는 않지만 예외를 던지는 다른 함수를 호출할 수 있음
다른 함수가 예외를 던지면 예외 중립적 함수는 그 예외를 그대로 통과시킴
이처럼 그냥 통과하는 예외가 존재할 수 있으므로 예외 중립적 함수는 <code>noexcept</code> 가 될 수 없음</p>
<p>만약 <code>noexcept</code> 로 선언하고 나중에 마음을 바꾸면 클라이언트 코드가 깨질 위험이 생김
<code>noexcept</code> 는 함수의 인터페이스의 일부로 함수의 구현이 예외를 방출하지 않는다는 성질을 오랫동안 유지할 결심이 선 경우에만 함수를 <code>noexcept</code> 로 선언해야 함</p>
<p>기본적으로 모든 메모리 해제 함수와 소멸자는 암묵적으로 <code>noexcept</code> 이기 때문에 직접 선언할 필요 없음
(직접 선언해도 해가 되지는 않지만 일반적으로 키워드를 쓰지 않음)</p>
<h3 id="정리">정리</h3>
<ul>
<li><code>noexcept</code> 는 함수의 인터페이스의 일부이며 호출자가 <code>noexcept</code> 여부에 의존할 수 있음</li>
<li><code>noexcept</code> 함수는 <code>비noexcept</code> 함수보다 최적화 여지가 큼</li>
<li><code>noexcept</code> 는 이동 연산자들과 <code>swap</code>, 메모리 해제 함수들 그리고 소멸자에 특히나 유용함</li>
<li>대부분의 함수는 예외에 중립적임</li>
</ul>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="스택-언와인딩stack-unwinding">스택 언와인딩(Stack Unwinding)</h4>
<ul>
<li>함수 호출 스택을 풀어서 해당 함수가 소유한 자원을 해제하고 소멸자를 호출하는 작업</li>
<li><code>noexcept</code> 함수에서는 이런 스택 언와인딩을 준비할 필요가 없기 때문에 컴파일러는 호출 스택을 간단히 처리할 수 있음</li>
<li><blockquote>
<p>결과적으로 오버헤드 감소 및 더 간결한 목적 코드를 생성할 수 있음</p>
</blockquote>
</li>
</ul>
<p>이 외에도 컴파일러에게 예외 처리와 관련된 메타데이터 및 런타임 동작을 제거할 수 있는 힌트를 줌</p>
<ul>
<li>함수 호출과 관련된 런타임 예외 검사 제거</li>
<li>예외 처리 테이블 생성 생략</li>
<li>더 단순한 레지스터 관리 및 코드를 인라인 처리 가능</li>
</ul>
<h4 id="목적-코드object-code">목적 코드(Object Code)</h4>
<p>컴파일러에서 번역 단위별로 컴파일을 수행한 결과 생성된 파일이 목적 파일인데 그 안에 포함된 것이 목적 코드임
CPU가 실행할 수 있는 형태에 가깝지만 완전한 실행파일은 아님</p>
<h4 id="목적-파일object-file-내부에는-어떤-정보가-들어갈까">목적 파일(Object File) 내부에는 어떤 정보가 들어갈까?</h4>
<ul>
<li>목적 코드</li>
<li>심볼 테이블 : 함수와 변수의 정의/참조 정보</li>
<li>재배치 정보 : 코드/데이터의 주소 재배치 정보</li>
<li>디버깅 정보 : 디버깅을 위한 메타데이터</li>
<li>섹션 헤더 : 각 섹션(코드, 데이터 등)의 위치와 크기 정보</li>
<li>라이브러리 참조 정보 : 필요한 외부 라이브러리 정보</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[const_iterator]]></title>
            <link>https://velog.io/@chowon_/constiterator</link>
            <guid>https://velog.io/@chowon_/constiterator</guid>
            <pubDate>Wed, 18 Dec 2024 01:32:05 GMT</pubDate>
            <description><![CDATA[<h3 id="iterator-보다-const_iterator를-선호하라">iterator 보다 const_iterator를 선호하라</h3>
<h4 id="const_iterator">const_iterator</h4>
<ul>
<li>const를 가리키는 포인터의 STL 버전</li>
<li>수정하면 안 되는 값을 가리킴</li>
</ul>
<pre><code class="language-cpp">auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);</code></pre>
<h3 id="추가학습">추가학습</h3>
<h4 id="begin-end-rbegin-등의-비멤버-함수를-멤버-함수보다-선호하라">begin, end, rbegin 등의 비멤버 함수를 멤버 함수보다 선호하라</h4>
<p>C++에서 <code>begin</code>과 <code>end</code> 같은 비멤버 함수 버전을 멤버 함수보다 선호하는 것은 주로 코드의 일관성, 유연성, 그리고 일반성을 높이기 위해 나온 관습임</p>
<ol>
<li>일관성
STL의 컨테이너들은 모두 <code>std::begin</code>과 <code>std::end</code>같은 비멤버 함수를 지원함
이러한 비멤버 함수는 컨테이너가 무엇이든 동일한 방식으로 사용될 수 있음</li>
</ol>
<pre><code class="language-cpp">std::vector&lt;int&gt; vec = { 1, 2, 3 };
auto it = std::begin(vec); // 비멤버 함수 사용

auto it = vec.begin();     // 멤버 함수 사용</code></pre>
<p>만약 멤버 함수만 사용한다면 특정 컨테이너나 클래스에 따라 <code>begin()</code>, <code>cbegin()</code>, <code>rbegin()</code>등을 명시적으로 호출해야 함
비멤버 함수를 사용하면 이러한 호출 방식의 차이를 줄이고, STL 스타일의 함수 호출 패턴을 유지할 수 있음</p>
<ol start="2">
<li>유연성
비멤버 함수인 <code>std::begin</code>과 <code>std::end</code>는 사용자 정의 타입에도 적용될 수 있도록 설계됨
이를 통해 커스터마이징된 타입에도 동일한 방식으로 사용할 수 있음
예를 들어, 사용자 정의 컨테이너 타입이 <code>std::begin</code>과 <code>std::end</code>를 오버로드하면 STL과 동일하게 동작할 수 있음<pre><code class="language-cpp">struct CustomContainer
{
 int data[3] = { 1, 2, 3 };
 int* begin() { return &amp;data[0]; }
 int* end() { return &amp;data[3]; }
};
</code></pre>
</li>
</ol>
<p>CustomContainer c;
for (auto it = std::begin(c); it != std::end(c); ++it)
{
    std::cout &lt;&lt; *it &lt;&lt; &quot; &quot;; // 1 2 3
}</p>
<pre><code>여기서 `std::begin`과 `std::end`을 사용하면 내부적으로 적절한 멤버함수를 호출해주므로 STL과 사용자 정의 컨테이너 간의 일관성을 유지할 수 있음

3. 범용성
비멤버 함수는 포인터와 같은 원시 배열에서도 사용할 수 있음
```cpp
int arr[] = { 1, 2, 3 };
auto it = std::begin(arr);   // arr의 시작 주소 반환
auto end_it = std::end(arr); // arr의 끝 주소 반환</code></pre><p>반면 멤버 함수는 이런 원시 타입에서 사용할 수 없음</p>
<h3 id="정리">정리</h3>
<ul>
<li>iterator 보다 const_iterator를 선호하라</li>
<li>최대한 일반적인 코드에서는 begin, end, rbegin 등의 비멤버 버전들을 해당 멤버 함수들보다 선호해라</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[const]]></title>
            <link>https://velog.io/@chowon_/const</link>
            <guid>https://velog.io/@chowon_/const</guid>
            <pubDate>Mon, 16 Dec 2024 07:22:56 GMT</pubDate>
            <description><![CDATA[<h3 id="낌새만-보이면-const를-들이대-보자">낌새만 보이면 const를 들이대 보자</h3>
<h4 id="const">const</h4>
<p>어떤 값이 불변이어야 하는 제작자의 의도를 컴파일러 및 다른 프로그래머와 나눌 수 있는 수단</p>
<p><code>const</code> 키워드가 <code>*</code> 보다 왼쪽에 있으면 포인터가 가리키는 대상이 상수임
<code>*</code> 보다 오른쪽에 있으면 포인터 자체가 상수임</p>
<pre><code class="language-cpp">char greeting[] = &quot;Hello&quot;;
char* p = greeting;             // 비상수 포인터, 비상수 데이터 (값, 주소 변경 가능)

const char* p = greeting;       // 비상수 포인터, 상수 데이터 (주소 변경 가능)

char* const p = greeting;       // 상수 포인터, 비상수 데이터 (값 변경 가능)

const char* const p = greeting; // 상수 포인터, 상수 데이터</code></pre>
<p>포인터가 가리키는 대상을 상수로 만들 때 두 가지 방법이 있지만 의미적인 차이는 전혀 없음</p>
<pre><code class="language-cpp">void f1(const Widget *pw); // (1)
void f2(Widget const *pw); // (2)</code></pre>
<h4 id="상수-멤버-함수">상수 멤버 함수</h4>
<p>멤버 함수에 붙는 <code>const</code>는 해당 멤버 함수가 상수 객체에 대해 호출될 함수임을 알려줌</p>
<p>상수 멤버 함수를 사용하는 이유</p>
<ul>
<li><p>클래스로 만들어진 객체를 변경할 수 있는 함수와 변경할 수 없는 함수를 사용자 측에 알려줌</p>
<br>
#### 물리적 상수성, 논리적 상수성</li>
<li><p>물리적 상수성
어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야 함 (정적 멤버 제외)
즉, 그 객체를 구상하는 비트들 중 어떤 것도 바꾸면 안 됨</p>
</li>
<li><p>논리적 상수성
상수 멤버 함수라고 해서 객체의 한 비트도 수정할 수 없는 것이 아니라 일부 몇 비트 정도는 바꿀 수 있되, 그것을 사용자 측에서 알아채지 못하게만 하면 상수 멤버 자격이 있음</p>
</li>
<li><blockquote>
<p>컴파일러가 물리적 상수성을 요구하는 경우 <code>mutable</code> 키워드를 사용하여 논리적 상수성만 지킬 수 있음</p>
<br>
</blockquote>
<h4 id="비상수-멤버-함수와-상수-멤버-함수의-구현부가-동일한-경우">비상수 멤버 함수와 상수 멤버 함수의 구현부가 동일한 경우</h4>
<p>비상수 멤버 함수를 캐스팅해서 상수 멤버 함수를 호출하면 됨
반대의 경우는 상수성을 제거하고 비상수 멤버를 호출하게 되는 것이므로 안정성에 문제가 있음</p>
</li>
</ul>
<pre><code class="language-cpp">class TextBlock
{
public:
    const char&amp; operator[](std::size_t position) const
    {
        return text[position];
    }

    const char&amp; operator[](std::size_t position)
    {
        return const_cast&lt;char&amp;&gt;(
            static_cast&lt;const TextBlock&amp;&gt;
            (*this)[position]
            );
    }
};</code></pre>
<ul>
<li>const를 붙이는 캐스팅은 안전한 타입 변환(비상수 객체 -&gt; 상수 객체)이므로 static_cast 사용</li>
<li>const를 제거하는 캐스팅은 const_cast 사용<br>
#### 정리</li>
<li>const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는 데 도움을 줌</li>
<li>const는 어떤 유효범위에 있는 객체에도 붙을 수 있으며, 함수 매개변수, 반환 타입, 멤버 함수에도 붙을 수 있음</li>
<li>컴파일러 쪽에서 보면 비트 수준 상수성을 지켜야 하지만, 논리적인 상수성을 이용해서 프로그래밍 해야함</li>
<li>상수 멤버 및 비상수 멤버 함수가 기능적으로 서로 똑같게 구현되어 있을 경우에는 코드 중복을 피하는 것이 좋은데, 이때 비상수 버전이 상수 버전을 호출하도록 만들어야 함</li>
</ul>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="mutable-쓸거면-그냥-const-안-쓰면-되는-거-아니야">mutable 쓸거면 그냥 const 안 쓰면 되는 거 아니야?</h4>
<p>단순히 <code>const</code>를 쓰지 않으면 객체의 상태 변경이 자유롭게 이루어지기 때문에 객체의 상태가 예기치 않게 변할 수 있음
<code>const</code>를 사용하는 목적은 객체의 외부에서 값이 바뀌지 않도록 보호하기 위해서임
<code>mutable</code>은 <code>const</code>로 선언된 객체 내부에서 특정 멤버만 수정할 수 있도록 허용하기 때문에 외부에서는 불변성을 유지하면서도 내부적으로 필요한 수정을 허용할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[#define, const, enum, inline]]></title>
            <link>https://velog.io/@chowon_/define-const-enum-inline</link>
            <guid>https://velog.io/@chowon_/define-const-enum-inline</guid>
            <pubDate>Fri, 13 Dec 2024 08:17:53 GMT</pubDate>
            <description><![CDATA[<h3 id="define을-쓰려거든-const-enum-inline을-떠올리자">#define을 쓰려거든 const, enum, inline을 떠올리자</h3>
<p>선행 처리자보다 컴파일러를 더 가까이 하자</p>
<h4 id="define">#define</h4>
<pre><code class="language-cpp">#define ASPECT_RATIO 1.653</code></pre>
<p><code>#define</code>을 사용하면 소스 코드가 컴파일러로 넘어가기 전에 선행 처리자가 기호식 이름을 숫자 상수로 변경함 (사용한 만큼 사본 생성)
-&gt; <code>ASPECT_RATIO</code>는 컴파일러가 쓰는 기호 테이블에 들어가지 않기 때문에 디버깅이 어려워짐</p>
<h4 id="const">const</h4>
<p>이런 문제는 상수를 통해 해결할 수 있음</p>
<pre><code class="language-cpp">const double AspectRatio = 1.653;</code></pre>
<p>상수를 사용하면 컴파일러의 기호 테이블에 들어감
상수 타입의 <code>ASPECT_RATIO</code>는 아무리 여러 번 쓰여도 사본은 딱 한 개만 생성됨</p>
<p>상수로 교체할 때 두 가지 경우를 조심해야 함
(1) 상수 포인터를 정의하는 경우</p>
<ul>
<li>상수 정의는 대게 헤더 파일에 넣으므로 포인터와 가리키는 대상을 const로 선언해야 함<pre><code class="language-cpp">// 이 경우에는 char* 기반 문자열보다는 string을 사용하는 것이 좋음
const char* const myName = &quot;Hello&quot;;
const std::string myName = &quot;Hello&quot;;</code></pre>
</li>
</ul>
<p>(2) 클래스 멤버로 상수를 정의하는 경우</p>
<ul>
<li>어떤 상수의 유효범위를 클래스로 한정하고자 할 때는 그 상수를 멤버로 만들어야 함</li>
<li>그 상수의 사본 개수가 한 개를 넘지 못하게 하고 싶은 경우엔 정적 멤버로 만들어야 함<pre><code class="language-cpp">// .h 파일
class GamePlayer
{
private:
  static const int NumTurns; // 상수 선언
  int scores[NumTurns];       // 상수를 사용하는 부분
};
</code></pre>
</li>
</ul>
<p>// .cpp 파일
const int GamePlayer::NumTurns = 5; // 상수 정의</p>
<pre><code>
#### enum
정수 상수를 가지고 다른 사람이 주소를 얻는다던지 참조자를 쓴다던지 하는 경우가 싫다면 enum을 사용하는 것이 좋음

```cpp
class GamePlayer
{
private:
    enum { NumTurns = 5 };
    int score[NumTurns];
};</code></pre><h4 id="define과-매크로-함수">#define과 매크로 함수</h4>
<pre><code class="language-cpp">#define CALL_WITH_MAX(a, b) f((a) &gt; (b) ? (a) : (b))

void f(int maxValue) 
{
    cout &lt;&lt; &quot;Max value: &quot; &lt;&lt; maxValue &lt;&lt; endl;
}

int main()
{
    int a = 5;
    int b = 0;
    CALL_WITH_MAX(++a, b);        // a가 두 번 증가
    CALL_WITH_MAX(++a, b + 10); // a가 한 번 증가
}</code></pre>
<p>f()가 호출되기 전에 a가 증가하는 횟수가 달라짐
이 방법 보다는 템플릿을 사용해서 함수의 동작 방식과 타입 안정성을 모두 취하는 것이 좋음</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
inline void callWithMax(const T&amp; a, const T&amp; b)
{
    f(a &gt; b ? a : b);
}</code></pre>
<p>동일한 타입의 객체 두 개를 인자로 받고 둘 중 큰 것을 f에 넘겨서 호출하는 구조임
함수 본문에 괄호를 할 필요도 없고, 인자를 여러 번 평가해도 값이 변할 걱정이 없음</p>
<h3 id="정리">정리</h3>
<ul>
<li>단순한 상수를 쓸 때는 #define 보다 const 객체 혹은 enum을 우선 생각하는 것이 좋음</li>
<li>함수처럼 쓰이는 매크로를 만들고 싶을 때는 #define 매크로보다 인라인 함수를 사용하는 것이 좋음</li>
</ul>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="선언과-정의">선언과 정의</h4>
<ul>
<li>선언<ul>
<li>변수나 함수가 프로그램에 존재함을 알리지만 메모리 공간을 할당하지 않음</li>
<li>컴파일러에게 이름과 타입을 알려주는 것</li>
</ul>
</li>
<li>정의<ul>
<li>변수나 함수에 실제 메모리 공간을 할당하고, 값이나 구현을 제공함</li>
</ul>
</li>
</ul>
<pre><code class="language-cpp">class GamePlayer
{
private:
    static const int NumTurns = 5; // 선언
};

const int GamePlayer::NumTurns;   // 정의</code></pre>
<p>왜 <code>static const int NumTurns = 5;</code>는 선언일까?
클래스 내부에서 정의된 것처럼 보이지만 컴파일러에게 실제 메모리 할당을 알려준 것은 아니기 때문에 선언임</p>
<h4 id="static-멤버-변수-static-const-멤버-변수">static 멤버 변수, static const 멤버 변수</h4>
<ul>
<li>static 멤버 변수<ul>
<li>클래스의 인스턴스가 아니라 클래스 자체에 속함</li>
<li>메모리 영역에 저장되며 클래스가 인스턴스화되지 않아도 존재함</li>
</ul>
</li>
<li>static const 멤버 변수<ul>
<li>상수이므로 컴파일 시점에 그 값을 알 수 있음</li>
<li>컴파일러는 상수 값을 코드에 직접 삽입하거나 변수의 메모리 할당 없이 최적화할 수 있음</li>
</ul>
</li>
</ul>
<h4 id="클래스-외부에서-정의를-명시해주는-이유">클래스 외부에서 정의를 명시해주는 이유</h4>
<ul>
<li>링킹 오류를 방지하기 위해<ul>
<li>컴파일러는 해당 변수의 값이 상수라는 것을 인식하지만 외부에서 정의 해주지 않으면 링커가 실제로 그 변수를 참조할 때 해당 변수를 찾을 수 없기 때문</li>
</ul>
</li>
</ul>
<pre><code class="language-cpp">class GamePlayer
{
private:
    static const int NumTurns;
};

int main() 
{
    cout &lt;&lt; GamePlayer::NumTurns &lt;&lt; &#39;\n&#39;;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/4bfae9cc-80e5-4e46-a9be-43e2dd3f17cb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비공개 함수, 삭제된 함수, override]]></title>
            <link>https://velog.io/@chowon_/%EB%B9%84%EA%B3%B5%EA%B0%9C-%ED%95%A8%EC%88%98-%EC%82%AD%EC%A0%9C%EB%90%9C-%ED%95%A8%EC%88%98-override</link>
            <guid>https://velog.io/@chowon_/%EB%B9%84%EA%B3%B5%EA%B0%9C-%ED%95%A8%EC%88%98-%EC%82%AD%EC%A0%9C%EB%90%9C-%ED%95%A8%EC%88%98-override</guid>
            <pubDate>Wed, 11 Dec 2024 05:03:46 GMT</pubDate>
            <description><![CDATA[<h3 id="정의되지-않은-비공개-함수보다-삭제된-함수를-선호하라">정의되지 않은 비공개 함수보다 삭제된 함수를 선호하라</h3>
<p>개발자가 특정 함수를 호출하지 못하게 하려면 함수를 선언하지 않는 것이 가장 간단함
하지만 C++이 필요에 따라 자동으로 생성하는 함수는 같은 방법으로 해결할 수 없음</p>
<p>C++98에서는 해당 함수의 접근 지정자를 private으로 변경하여 호출을 방지했음 -&gt; 비공개 함수
C++11에서는 함수의 선언 끝에 <code>= delete;</code> 를 붙여서 호출을 방지함 -&gt; 삭제된 함수</p>
<h4 id="비공개-함수와-삭제된-함수의-차이">비공개 함수와 삭제된 함수의 차이</h4>
<p>(1) 삭제된 함수</p>
<ul>
<li>어떤 방법으로든 사용 불가 (멤버 함수나 friend에서도 접근 불가)</li>
<li>어떤 함수도 삭제 가능 (비 멤버 함수, 템플릿 인스턴스 포함)</li>
</ul>
<pre><code class="language-cpp">// 비 멤버 함수에서 특정 형식의 함수 제거하기
bool isLucky(int number);
bool isLucky(char) = delete;   // char 배제
bool isLucky(bool) = delete;   // bool 배제
bool isLucky(double) = delete; // double, float 배제</code></pre>
<p>(2) 비공개 함수</p>
<ul>
<li>멤버 함수에만 적용 가능</li>
<li>멤버 함수나 friend에서 접근 가능</li>
</ul>
<h4 id="삭제된-함수는-왜-public으로-선언하는-것이-관례일까">삭제된 함수는 왜 public으로 선언하는 것이 관례일까?</h4>
<p>클라이언트 코드가 멤버 함수를 사용하려 할 때 C++은 먼저 그 함수의 접근성을 점검하고 그 후 삭제 여부를 점검함
삭제된 함수를 private으로 설정하는 경우 함수를 사용할 수 없는 주된 이유가 함수의 접근성이 되므로 오해의 여지를 제공함</p>
<h3 id="재정의-함수들을-override로-선언하라">재정의 함수들을 override로 선언하라</h3>
<h4 id="재정의가-일어나기-위한-필수조건">재정의가 일어나기 위한 필수조건</h4>
<ul>
<li>기반 클래스 함수가 반드시 가상 함수여야 함</li>
<li>기반 함수와 파생 함수의 이름이 반드시 동일해야 함 (단, 소멸자는 예외)</li>
<li>기반 함수와 파생 함수의 매개변수 형식들이 반드시 동일해야 함</li>
<li>기반 함수와 파생 함수의 const성이 반드시 동일해야 함</li>
<li>기반 함수와 파생 함수의 반환 형식과 예외 명세(exception specification)가 반드시 호환되어야 함</li>
<li>멤버 함수들의 참조 한정자들이 반드시 동일해야 함</li>
</ul>
<pre><code class="language-cpp">class Base
{
public:
    virtual void mf1() const;
    virtual void mf2(int x);
    virtual void mf3() &amp;;
    void mf4() const;
};

class Derived : public Base
{
public:
    virtual void mf1();                 // 부모 클래스와 달리 const 선언되지 않음
    virtual void mf2(unsigned int x);    // 부모 클래스와 달리 unsigned int를 받음
    virtual void mf3() &amp;&amp;;                // 부모 클래스와 달리 오른값으로 한정됨
    void mf4() const;                    // 부모 클래스가 virtual로 선언되지 않음
};</code></pre>
<p><code>override</code> 키워드를 사용하면 재정의하려 한다는 의도를 명시적으로 표현할 수 있음</p>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="예외-명세exception-specification란">예외 명세(exception specification)란?</h4>
<p><code>noexcept</code> 키워드를 사용하여 예외를 던질 수 있는지 여부를 표기하는 방법
(1) 장점</p>
<ul>
<li>컴파일러가 예외 처리와 관련된 최적화를 수행할 수 있음</li>
<li>함수 인터페이스를 명확히하여 호출자가 외예 발생 여부를 쉽게 판단할 수 있음</li>
<li>동적 조건을 넣어서 조건부로 적용될 수 있음</li>
</ul>
<p>다형성으로 인해 기반 클래스 포인터를 사용하여 파생 클래스의 객체를 조작할 수 있는데 이 때 정의된 제약 조건이 깨지면 프로그램의 안정성을 해치고 예외 처리를 어렵게 함</p>
<h4 id="멤버-함수-참조-한정자reference-qualifier란">멤버 함수 참조 한정자(reference qualifier)란?</h4>
<p>멤버 함수가 호출되는 객체(*this)의 왼값 버전과 오른값 버전을 다른 방식으로 처리할 수 있음</p>
<pre><code class="language-cpp">class Widget
{
public:
    void doWork()&amp; // *this가 왼값일 때 호출되는 함수
    {
        cout &lt;&lt; &quot;doWork() called on lvalue&quot; &lt;&lt; endl;
    }

    void doWork()&amp;&amp; // *this가 오른값일 때 호출되는 함수
    {
        cout &lt;&lt; &quot;doWork() called on rvalue&quot; &lt;&lt; endl;
    }
};

Widget makeWidget()
{
    return Widget();
}

int main()
{
    Widget w;                 // 왼 값을 돌려주는 객체

    w.doWork();                // Widget::doWork &amp;를 호출함
    makeWidget().doWork(); // Widget::doWork &amp;&amp;를 호출함
}</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/64688cdc-4b67-4df9-b13c-94f72a492506/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[typedef, using, enum]]></title>
            <link>https://velog.io/@chowon_/typedef-using-enum</link>
            <guid>https://velog.io/@chowon_/typedef-using-enum</guid>
            <pubDate>Tue, 10 Dec 2024 04:36:06 GMT</pubDate>
            <description><![CDATA[<h3 id="typedef보다-별칭-선언을-선호하라">typedef보다 별칭 선언을 선호하라</h3>
<h4 id="typedef-별칭-선언alias-declaration">typedef, 별칭 선언(alias declaration)</h4>
<table>
<thead>
<tr>
<th>기능</th>
<th align="center">typedef</th>
<th align="center">using</th>
</tr>
</thead>
<tbody><tr>
<td>사용자 정의 타입에 새로운 이름 부여</td>
<td align="center">O</td>
<td align="center">O</td>
</tr>
<tr>
<td>별칭 템플릿 가능</td>
<td align="center">X</td>
<td align="center">O</td>
</tr>
</tbody></table>
<pre><code class="language-cpp">#include &lt;memory&gt;
#include &lt;unordered_map&gt;
#include &lt;string&gt;

// 새로운 이름 부여
// typedef
typedef std::unique_ptr&lt;std::unordered_map&lt;std::string, std::string&gt;&gt; UPtrMapSS;

// alias declaration
using UPtrMapSS = std::unique_ptr&lt;std::unordered_map&lt;std::string, std::string&gt;&gt;;

// 함수 포인터
// typedef
typedef void (*FP)(int, const std::string&amp;);

// alias declaration
using FP = void (*)(int, const std::string&amp;);

// 템플릿
// typedef는 템플릿화 불가

// alias declaration
class Widget{};

template&lt;typename T&gt;
using MyAllocList = std::list&lt;T, MyAlloc&lt;T&gt;&gt;;

MyAllocList&lt;Widget&gt; lw; // std::list&lt;Widget, MyAlloc&lt;Widget&gt;&gt; lw; 와 동일</code></pre>
<h3 id="범위-없는-enum보다-범위-있는-enum을-선호하라">범위 없는 enum보다 범위 있는 enum을 선호하라</h3>
<h4 id="범위-없는-enum">범위 없는 enum</h4>
<p>일반적으로 중괄호 쌍 안에서 어떤 이름을 선언하면 그 이름의 가시성은 해당 중괄호 쌍이 정의하는 범위로 한정됨
그런데 C++98 스타일의 enum은 그러한 일반적인 규칙이 적용되지 않음
enum을 포함하는 범위에 속하게 되며 해당 범위 내 같은 이름이 존재하면 안 됨
=&gt; 이러한 특성 때문에 범위 없는 enum이라고 부름</p>
<pre><code class="language-cpp">enum Color { black, white, red };
auto white = false; // 오류 범위 내에 이미 white이 있음</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/ecc50189-7022-45df-99c2-40eee7383931/image.png" alt=""></p>
<p>암묵적으로 다른 형식으로 변환됨</p>
<pre><code class="language-cpp">enum Color { black, white, red };

std::vector&lt;std::size_t&gt; primeFactors(std::size_t x);

Color c = red;

if ( c &lt; 14.5 )
{
    auto factors = primeFactors(c);
}</code></pre>
<p>C++11부터는 이름 없는 enum도 전방 선언이 가능함</p>
<h4 id="범위-있는-enum">범위 있는 enum</h4>
<p>C++11의 새로운 열거형인 범위 있는 enum은 그러한 이름 누수가 발생되지 않음
암묵적으로 다른 형식으로 변환되지 않음
전방 선언이 가능함
바탕 형식을 지정할 수 있음</p>
<pre><code class="language-cpp">enum class Color { black, white, red };
auto white = false;     // OK
Color a = white;         // 오류 범위 있는 enum은 정수형으로 변환 X
Color b = Color::white; // OK
auto c = Color::white;  // OK</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/64642956-9722-493c-8276-8e8b906c69d2/image.png" alt=""></p>
<pre><code class="language-cpp">enum class Color { black, white, red };

std::vector&lt;std::size_t&gt; primeFactors(std::size_t x);

Color c = Color::red;

if (c &lt; 14.5) // 오류 Color와 double 비교 불가
{
    auto factors = primeFactors(c); // 오류 std::size_t를 기대하는 함수에 Color 전달 불가
}

// 만약 다른 형식으로 변환하고 싶다면 캐스팅을 해야함
if (static_cast&lt;double&gt;(c) &lt; 14.5)
{
    auto factors = primeFactors(static_cast&lt;std::size_t&gt;(c));
}</code></pre>
<p>전방 선언이 가능하기 때문에 열거자들을 지정하지 않고 열거형 이름만 미리 선언 가능</p>
<pre><code class="language-cpp">enum Color;       // C++98기준 오류 C++11부터는 가능
enum class Color; // OK</code></pre>
<p>기본 바탕 형식은 int이지만 다른 형식을 명시적으로 지정할 수 있음</p>
<pre><code class="language-cpp">enum class Status: std::uint32_t;</code></pre>
<h4 id="범위-없는-enum은-언제-쓸까">범위 없는 enum은 언제 쓸까?</h4>
<p>튜플 안에 있는 필드들을 지칭할 때 유용함</p>
<pre><code class="language-cpp">// 정수를 사용하는 경우 -&gt; 1번 필드가 뭔지 기억하기 어려움
using UserInfo = std::tuple&lt;std::string, std::string, std::size_t&gt;;
UserInfo uInfo;

auto val = std::get&lt;1&gt;(uInfo);

// 범위 없는 enum 사용 -&gt; 명확히 어떤 필드를 가져오는건지 알 수 있음
enum UserInfoFields { uiName, uiEmail, uiReputation };
auto val = std::get&lt;uiEmail&gt;(uInfo); // 암묵적 형변환이 가능하기 때문

// 만약 범위 있는 enum을 사용한다면 캐스팅을 하거나
enum class UserInfoFields { uiName, uiEmail, uiReputation };
auto val = std::get&lt;static_cast&lt;std::size_t&gt;(UserInfoFields::uiEmail)&gt;(uInfo);

// 함수를 만들어야 함
// 변환하는 함수는 컴파일 시점에 산출해야 하므로 constexpr 함수여야 함
// 어떤 종류에도 동작하기 위해서는 함수 템플릿 형태여야 하며
// 예외를 던지지 않을 것이므로 noexcept로 선언해야 함
template&lt;typename E&gt;
constexpr auto toUType(E enumerator) noexcept
{
    return static_cast&lt;typename std::underlying_type&lt;E&gt;::type&gt;(enumerator);
}
auto val = std::get&lt;toUType(UserInfoFields::uiEmail)&gt;(uInfo);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0, NULL, nullptr]]></title>
            <link>https://velog.io/@chowon_/0-NULL-nullptr</link>
            <guid>https://velog.io/@chowon_/0-NULL-nullptr</guid>
            <pubDate>Sun, 08 Dec 2024 05:12:11 GMT</pubDate>
            <description><![CDATA[<h3 id="0과-null보다-nullptr을-선호하라">0과 NULL보다 nullptr을 선호하라</h3>
<h4 id="0">0</h4>
<ul>
<li>리터럴 0은 int이지 포인터가 아님</li>
<li>포인터만 사용할 수 있는 위치에 0이 있으면 마지못해 nullptr로 해석하긴 하지만 최후의 수단일 뿐임</li>
</ul>
<h4 id="null">NULL</h4>
<ul>
<li>NULL도 포인터 형식이 아님</li>
</ul>
<pre><code class="language-cpp">void f(int);
void f(bool);
void f(void*);

f(0);          // f(void*)가 아니라 f(int) 호출

f(NULL);       // 컴파일되지 않을 수도 있지만, 보통은 f(int)를 호출함
               // f(void*)를 호출하는 경우는 없음

f(nullptr);    // f(void*)가 호출됨</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/ec3480f4-4b49-47fb-b39d-7eb41b3ef57b/image.png" alt=""></p>
<h4 id="nullptr">nullptr</h4>
<ul>
<li>정수도 포인터 형식도 아님</li>
<li>std::nullptr_t인데 이는 모든 raw pointer 형식으로 암묵적 변환됨
=&gt; 모든 형식의 포인터처럼 행동함 (중복 적재가 예상과 일치)</li>
<li>코드의 명확성을 높여줌</li>
</ul>
<pre><code class="language-cpp">// (1) auto 변수가 관여하는 상황
auto result = findRecord(/*인수들*/);

// 이렇게 사용하면 findRecord 반환 형식을 모르거나 파악하기 어려운 경우
// 반환값이 포인터 형식인지 정수 형식인지 명확하지 않음
if (result == 0)
{
    // ...
}

// 이렇게 사용하면 중의성이 없음
if (result == nullptr)
{
    // ...
}

// (2) 템플릿이 관여하는 상황
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;memory&gt;
#include &lt;mutex&gt;

using namespace std;

class Widget {};

int f1(std::shared_ptr&lt;Widget&gt; spw);
double f2(std::unique_ptr&lt;Widget&gt; upw);
bool f3(Widget* pw);

std::mutex f1m, f2m, f3m;

template&lt;typename FuncType, typename MuxType, typename PtrType&gt;
auto lockAndCall(FuncType func, MuxType&amp; mutex, PtrType ptr) -&gt; decltype(func(ptr))
{
    using MuxGuard = std::lock_guard&lt;MuxType&gt;;

    MuxGuard g(mutex);
    return func(ptr);
}

int main()
{
    auto result1 = lockAndCall(f1, f1m, 0);       // (1) 오류
    auto result2 = lockAndCall(f2, f2m, NULL);    // (2) 오류
    auto result3 = lockAndCall(f3, f3m, nullptr); // (3) OK
}</code></pre>
<p>(1), (2) 호출의 문제
<code>lockAndCall</code> 함수에 <code>0</code>이나 <code>NULL</code>을 넘겨주면 컴파일러는 형식을 파악하기 위해 템플릿 형식 연역을 적용함
두 타입 모두 정수 형식으로 연역되기 때문에 ptr로 넘겨주면 형식 오류가 발생함</p>
<p>(3)의 <code>nullptr</code>은 <code>std::nullptr_t</code>로 연역되므로 <code>Widget*</code>로 암묵적 변환이 일어나 문제가 발생하지 않음</p>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="중복-적재">중복 적재</h4>
<p>함수 오버로딩을 지칭하는 다른 용어라고 함
함수 오버로딩은 동일한 함수 이름을 가진 여러 함수를 정의하고 매개변수의 타입 또는 개수에 따라 적절한 함수를 호출하도록 하는 기능임</p>
<h4 id="중복-적재의-동작-방식">중복 적재의 동작 방식</h4>
<p>코드 상에서는 동일한 함수 이름처럼 보이지만, 컴파일러는 함수의 시그니처(함수 이름, 매개변수 타입, 반환 타입)를 기준으로 각 함수에 고유한 내부 이름을 부여함 -&gt; 네임 맹글링</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Uniform Initialization]]></title>
            <link>https://velog.io/@chowon_/Uniform-Initialization</link>
            <guid>https://velog.io/@chowon_/Uniform-Initialization</guid>
            <pubDate>Thu, 05 Dec 2024 08:29:40 GMT</pubDate>
            <description><![CDATA[<h3 id="객체-생성-시-괄호와-중괄호를-구분하라">객체 생성 시 괄호와 중괄호를 구분하라</h3>
<h4 id="uniform-initializationbr">uniform initialization<br></h4>
<ul>
<li>C++은 초기화 할 때 =, (), {}를 지원함</li>
<li>그 중에서 어디서나 사용할 수 있는 것이 중괄호 구문임</li>
<li>(장점) =, ()와 달리 암묵적 좁히기 변환(<strong>narrowing conversion</strong>)을 방지해 줌</li>
<li>(단점) 종종 예상치 못한 행동을 보임 (예) std::initializer_list 선호 현상</li>
</ul>
<h4 id="narrowing-conversion이란">narrowing conversion이란?</h4>
<ul>
<li>더 큰 범위 또는 더 넓은 정밀도를 가진 데이터 타입을 더 작은 범위 또는 더 낮은 정밀도를 가진 데이터 타입으로 변환하는 것</li>
<li>이 과정에서 데이터가 손실되거나 값이 달라질 수 있으므로 위험한 변환임<pre><code class="language-cpp">// 실수에서 정수로 변환하는 경우
double d = 3.14;
int i = d; // i는 3이됨
</code></pre>
</li>
</ul>
<p>// uniform initialization은 암묵적 좁히기 변환을 방지함
int x = 3.14; // 허용 x는 3
int y(3.14);  // 허용 y는 3
int z{3.14};  // 컴파일 오류</p>
<pre><code>#### uniform initialization의 예기치 못한 동작
```cpp
class Widget
{
public:
    Widget(int i, bool b);   // (1)
    Widget(int i, double d); // (2)
    // ...
};

Widget w1(10, true);  // (1) 호출
Widget w2{10, true};  // (1) 호출
Widget w3(10, 5.0);   // (2) 호출
Widget w4{10, 5.0};   // (2) 호출</code></pre><p><img src="https://velog.velcdn.com/images/chowon_/post/fddb3867-d7fd-4891-8b26-10b8755a2d47/image.png" alt=""></p>
<p>여기 까지는 예상대로 동작함
그러나 생성자 중 하나 이상이 <code>std::initializer_list</code> 형식의 매개변수를 선언하면 초기화 구문은 이 버전을 강하게 선호함</p>
<pre><code class="language-cpp">class Widget
{
public:
    Widget(int i, bool b);                           // (1)
    Widget(int i, double d);                         // (2)
    Widget(std::initializer_list&lt;long double&gt; il);  // (3)
};

Widget w1(10, true);  // (1) 호출
Widget w2{10, true};  // (3) 호출 10과 true가 long double로 변환됨
Widget w3(10, 5.0);   // (2) 호출
Widget w4{10, 5.0};   // (3) 호출 10과 5.0이 long double로 변환됨</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/8c10be9e-8328-46ca-9582-1c2c97e1224c/image.png" alt=""></p>
<p>생성자 중 정확히 형식이 일치하는 경우가 있음에도 불구하고 <code>std::initializer_list&lt;bool&gt;</code>
을 호출하려고 함
<code>bool</code>로 변환하기 위해서는 좁히기 변환이 필요하지만 <code>uniform initialization</code>은 이를 방지하므로 컴파일 에러가 발생함 (정확히 형식이 일치하는 생성자가 있음에도 불구하고.. 🤔)</p>
<pre><code class="language-cpp">class Widget
{
public:
    Widget(int i, bool b);                           // (1)
    Widget(int i, double d);                         // (2)
    Widget(std::initializer_list&lt;bool&gt; il);          // (3)
};

Widget w4{10, 5.0};   // 컴파일 에러</code></pre>
<p>우선 순위를 가지는 정도가 아니라 다른 생성자가 고려 대상에서 제외될 정도로 가려버림</p>
<p>이 문제에 직접 영향을 받는 클래스 중 하나가 <code>vector</code>임
<code>vector</code>는 <code>비std::initializer_list</code> 생성자와 <code>std::initializer_list</code> 생성자 둘 다 있기 때문</p>
<pre><code class="language-cpp">vector&lt;int&gt; v1(1, 2); // 비 std::initializer_list 생성자 사용
                      // 모든 요소의 값이 2인 요소 1개짜리 vector가 생성됨

vector&lt;int&gt; v2{1, 2}; // std::initializer_list 생성자 사용
                      // 값이 1, 2인 두 요소를 담은 vector가 생성됨</code></pre>
<p><img src="https://velog.velcdn.com/images/chowon_/post/57794687-afa4-4873-9896-d3185b63475f/image.png" alt=""></p>
<p>=&gt; 클래스를 작성할 때 생성자 중에 <code>std::initializer_list</code>를 받는 함수가 하나 이상 존재한다면 중괄호 초기화 사용을 주의해야 함을 기억하자</p>
<h3 id="추가-학습">추가 학습</h3>
<h4 id="stdinitializer_list란">std::initializer_list란?</h4>
<ul>
<li>C++11에서 도입된 클래스 템플릿</li>
<li>{}를 이용해서 생성자를 호출할 때, 클래스의 생성자들 중에 <code>std::initializer_list</code>를 인자로 받는 생성자가 있다면 전달됨</li>
<li>컨테이너와 유사한 동작이 가능 (내부적으로 begin(), end() 제공함)</li>
<li>불변 데이터 구조를 가짐 (값을 복사하여 저장하므로 생성된 이후에는 변경 불가)</li>
<li>가변 개수의 인자 처리</li>
</ul>
<pre><code class="language-cpp">initializer_list&lt;int&gt; il = { 1, 2, 3 };
il.begin()[0] = 10; // 컴파일 오류</code></pre>
<h4 id="vector를-사용할-때-uniform-initialization을-사용하는-경우-stdinitializer_list-생성자가-호출되는데-왜-값-변경이-가능할까">vector를 사용할 때 uniform initialization을 사용하는 경우 std::initializer_list 생성자가 호출되는데 왜 값 변경이 가능할까?</h4>
<p>초기화는 std::initializer_list 생성자로 호출되지만 값을 복사하여 vector에 데이터를 저장하기 때문에 생성된 이후 initializer_list와 상관없이 값 변경이 가능함</p>
<p>=&gt; <code>std::initializer_list</code>는 불변이지만 이를 사용해 초기화된 컨테이너는 컨테이너의 특성에 따라 값을 변경할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[형식 연역]]></title>
            <link>https://velog.io/@chowon_/%ED%98%95%EC%8B%9D-%EC%97%B0%EC%97%AD</link>
            <guid>https://velog.io/@chowon_/%ED%98%95%EC%8B%9D-%EC%97%B0%EC%97%AD</guid>
            <pubDate>Mon, 11 Nov 2024 04:53:07 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>형식 연역이란?</p>
<ul>
<li>컴파일러가 변수나 함수의 타입을 자동으로 추론하는 과정</li>
<li>함수 템플릿 호출 지점, auto, decltype 표현식 등에서 발생함<br>
</li>
</ul>
</li>
<li><p>템플릿 형식 연역 규칙</p>
<ul>
<li><p>컴파일러는 expr를 이용하여 T와 paramType을 연역함</p>
</li>
<li><p>(경우 1) paramType이 포인터 또는 참조 형식이지만 <strong>보편 참조</strong>는 아닌 경우</p>
<ul>
<li>규칙</li>
<li>expr 형식이 참조 형식이면 참조 부분 무시 </li>
</ul>
</li>
<li><p>(경우 2) paramType이 보편 참조인 경우</p>
<ul>
<li>규칙</li>
<li>expr가 lvalue면 T와 paramType 둘 다 lvalue 참조로 연역</li>
<li>expr가 rvalue면 (경우 1)의 규칙 적용 (참조, const, volatile 떼기)</li>
</ul>
</li>
<li><p>(경우 3) paramType이 포인터도 아니고 참조도 아닌 경우</p>
<ul>
<li>규칙</li>
<li>expr 형식이 참조면 참조 부분 무시</li>
<li>expr 형식과 paramType을 비교하여 T의 형식 추론 (const, volatile 한정자 제거)</li>
</ul>
</li>
<li><p>(그 외)</p>
<ul>
<li><p>배열 인수</p>
</li>
<li><p>배열 형식은 포인터 형식과 다름</p>
<ul>
<li>맞바꿔 쓸 수 있는 것처럼 보이는 건 많은 문맥에서 배열이 배열의 첫 원소를 가리키는 포인터로 붕괴하기 때문</li>
</ul>
</li>
<li><p>규칙</p>
<ul>
<li>값 전달 매개변수가 있는 템플릿은 함수 호출 시 배열의 첫 번째 원소를 가리키는 포인터로 자동 변환됨</li>
<li>참조 매개변수가 있는 템플릿은 배열이 붕괴되지 않고 그대로 전달됨</li>
</ul>
</li>
<li><p>함수 인수</p>
</li>
<li><p>함수 형식도 함수 포인터로 붕괴할 수 있음</p>
</li>
<li><p>규칙</p>
<ul>
<li>값 전달 매개변수가 있는 템플릿은 함수 포인터로 연역됨</li>
<li>참조 매개변수가 있는 템플릿은 함수 참조로 연역됨</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규 표현식]]></title>
            <link>https://velog.io/@chowon_/%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@chowon_/%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Mon, 04 Nov 2024 01:44:21 GMT</pubDate>
            <description><![CDATA[<h3 id="정규-표현식">정규 표현식</h3>
<p>문자열에서 패턴을 찾을 때 사용함
정규 표현식을 통해 문자열이 주어진 규칙에 맞는지 확인할 수 있음</p>
<h3 id="db-숫자-logtxt-형태인지-확인하기">db-숫자-log.txt 형태인지 확인하기</h3>
<pre><code class="language-cpp">// \d* : 임의의 개수의 숫자
// .앞에 \를 붙인 이유는 임의의 문자로 해석되는 것을 방지하기 위함
db-\d*-log\.txt</code></pre>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;regex&gt;
#include &lt;vector&gt;

using namespace std;

int main()
{
    vector&lt;string&gt; file_names = { &quot;db-123-log.txt&quot;, &quot;db-124-log.txt&quot;,
                                 &quot;not-db-log.txt&quot;, &quot;db-12-log.txt&quot;,
                                 &quot;db-12-log.jpg&quot; };

    regex re(&quot;db-\\d*-log\\.txt&quot;);

    for (const auto&amp; file_name : file_names)
    {
        // regex_match : 첫 번째 인자로 전달된 문자열이 두 번째 인자로 전달된 정규 표현식 객체와        
        //               완전히 매칭되는 경우 true 리턴
        cout &lt;&lt; file_name &lt;&lt; &quot;: &quot; &lt;&lt; std::boolalpha &lt;&lt; std::regex_match(file_name, re) &lt;&lt; &#39;\n&#39;;
    }
}</code></pre>
<br>

<h3 id="실행-결과">실행 결과</h3>
<p><img src="https://velog.velcdn.com/images/chowon_/post/0a96f719-7d11-451f-b0b5-06c3999f2aa8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[행렬의 곱셈]]></title>
            <link>https://velog.io/@chowon_/%ED%96%89%EB%A0%AC%EC%9D%98-%EA%B3%B1%EC%85%88</link>
            <guid>https://velog.io/@chowon_/%ED%96%89%EB%A0%AC%EC%9D%98-%EA%B3%B1%EC%85%88</guid>
            <pubDate>Thu, 05 Sep 2024 07:05:56 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>2차원 행렬 arr1과 arr2를 입력받아, arr1에 arr2를 곱한 결과를 반환하는 함수, solution을 완성해주세요.</p>
<h3 id="제한-조건">제한 조건</h3>
<p>행렬 arr1, arr2의 행과 열의 길이는 2 이상 100 이하입니다.
행렬 arr1, arr2의 원소는 -10 이상 20 이하인 자연수입니다.
곱할 수 있는 배열만 주어집니다.</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

vector&lt;vector&lt;int&gt;&gt; solution(vector&lt;vector&lt;int&gt;&gt; arr1, vector&lt;vector&lt;int&gt;&gt; arr2) {
    vector&lt;vector&lt;int&gt;&gt; answer(arr1.size(), vector&lt;int&gt;(arr2[0].size()));

    for(int i = 0; i &lt; arr1.size(); i++)
    {
        for(int j = 0; j &lt; arr2.size(); j++)
        {
            for(int k = 0; k &lt; arr2[0].size(); k++)
            {
                answer[i][k] += arr1[i][j] * arr2[j][k];
            }
        }
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[n^2 배열 자르기]]></title>
            <link>https://velog.io/@chowon_/n2-%EB%B0%B0%EC%97%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@chowon_/n2-%EB%B0%B0%EC%97%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Thu, 05 Sep 2024 06:23:51 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>정수 n, left, right가 주어집니다. 다음 과정을 거쳐서 1차원 배열을 만들고자 합니다.</p>
<p>n행 n열 크기의 비어있는 2차원 배열을 만듭니다.
i = 1, 2, 3, ..., n에 대해서, 다음 과정을 반복합니다.
1행 1열부터 i행 i열까지의 영역 내의 모든 빈 칸을 숫자 i로 채웁니다.
1행, 2행, ..., n행을 잘라내어 모두 이어붙인 새로운 1차원 배열을 만듭니다.
새로운 1차원 배열을 arr이라 할 때, arr[left], arr[left+1], ..., arr[right]만 남기고 나머지는 지웁니다.
정수 n, left, right가 매개변수로 주어집니다. 주어진 과정대로 만들어진 1차원 배열을 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>1 ≤ n ≤ 107
0 ≤ left ≤ right &lt; n2
right - left &lt; 105</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

vector&lt;int&gt; solution(int n, long long left, long long right) {
    vector&lt;int&gt; answer;

    for(long long k = left; k &lt;= right; k++)
    {
        int i = k / n;
        int j = k % n;

        answer.push_back(max(i, j) + 1);
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[할인 행사]]></title>
            <link>https://velog.io/@chowon_/%ED%95%A0%EC%9D%B8-%ED%96%89%EC%82%AC</link>
            <guid>https://velog.io/@chowon_/%ED%95%A0%EC%9D%B8-%ED%96%89%EC%82%AC</guid>
            <pubDate>Mon, 02 Sep 2024 05:39:41 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>XYZ 마트는 일정한 금액을 지불하면 10일 동안 회원 자격을 부여합니다. XYZ 마트에서는 회원을 대상으로 매일 한 가지 제품을 할인하는 행사를 합니다. 할인하는 제품은 하루에 하나씩만 구매할 수 있습니다. 알뜰한 정현이는 자신이 원하는 제품과 수량이 할인하는 날짜와 10일 연속으로 일치할 경우에 맞춰서 회원가입을 하려 합니다.</p>
<p>예를 들어, 정현이가 원하는 제품이 바나나 3개, 사과 2개, 쌀 2개, 돼지고기 2개, 냄비 1개이며, XYZ 마트에서 14일간 회원을 대상으로 할인하는 제품이 날짜 순서대로 치킨, 사과, 사과, 바나나, 쌀, 사과, 돼지고기, 바나나, 돼지고기, 쌀, 냄비, 바나나, 사과, 바나나인 경우에 대해 알아봅시다. 첫째 날부터 열흘 간에는 냄비가 할인하지 않기 때문에 첫째 날에는 회원가입을 하지 않습니다. 둘째 날부터 열흘 간에는 바나나를 원하는 만큼 할인구매할 수 없기 때문에 둘째 날에도 회원가입을 하지 않습니다. 셋째 날, 넷째 날, 다섯째 날부터 각각 열흘은 원하는 제품과 수량이 일치하기 때문에 셋 중 하루에 회원가입을 하려 합니다.</p>
<p>정현이가 원하는 제품을 나타내는 문자열 배열 want와 정현이가 원하는 제품의 수량을 나타내는 정수 배열 number, XYZ 마트에서 할인하는 제품을 나타내는 문자열 배열 discount가 주어졌을 때, 회원등록시 정현이가 원하는 제품을 모두 할인 받을 수 있는 회원등록 날짜의 총 일수를 return 하는 solution 함수를 완성하시오. 가능한 날이 없으면 0을 return 합니다.</p>
<h3 id="제한사항">제한사항</h3>
<p>1 ≤ want의 길이 = number의 길이 ≤ 10
1 ≤ number의 원소 ≤ 10
number[i]는 want[i]의 수량을 의미하며, number의 원소의 합은 10입니다.
10 ≤ discount의 길이 ≤ 100,000
want와 discount의 원소들은 알파벳 소문자로 이루어진 문자열입니다.
1 ≤ want의 원소의 길이, discount의 원소의 길이 ≤ 12</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;map&gt;
#include &lt;iostream&gt;

using namespace std;

bool isValid(map&lt;string, int&gt;&amp; m)
{
    bool allDiscount = true;
    for(auto it : m)
    {
        if(it.second &gt; 0)
        {
            allDiscount = false;
            break;
        }
    }

    return allDiscount;
}

int solution(vector&lt;string&gt; want, vector&lt;int&gt; number, vector&lt;string&gt; discount) {
    map&lt;string/*물건이름*/, int/*개수*/&gt; m;

    for(int i = 0; i &lt; want.size(); i++)
    {
        m[want[i]] = number[i];
    }

    for(int i = 0; i &lt; 10; i++)
    {
        if(m.end() != m.find(discount[i])) --m[discount[i]];
    }

    int answer = 0;
    for(int i = 10; i &lt; discount.size(); i++)
    {
        answer += isValid(m);

        if(m.end() != m.find(discount[i - 10])) ++m[discount[i - 10]];
        if(m.end() != m.find(discount[i])) --m[discount[i]];
    }

    answer += isValid(m);

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[괄호 회전하기]]></title>
            <link>https://velog.io/@chowon_/%EA%B4%84%ED%98%B8-%ED%9A%8C%EC%A0%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@chowon_/%EA%B4%84%ED%98%B8-%ED%9A%8C%EC%A0%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Sep 2024 05:21:23 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>다음 규칙을 지키는 문자열을 올바른 괄호 문자열이라고 정의합니다.</p>
<p>(), [], {} 는 모두 올바른 괄호 문자열입니다.
만약 A가 올바른 괄호 문자열이라면, (A), [A], {A} 도 올바른 괄호 문자열입니다. 예를 들어, [] 가 올바른 괄호 문자열이므로, ([]) 도 올바른 괄호 문자열입니다.
만약 A, B가 올바른 괄호 문자열이라면, AB 도 올바른 괄호 문자열입니다. 예를 들어, {} 와 ([]) 가 올바른 괄호 문자열이므로, {}([]) 도 올바른 괄호 문자열입니다.
대괄호, 중괄호, 그리고 소괄호로 이루어진 문자열 s가 매개변수로 주어집니다. 이 s를 왼쪽으로 x (0 ≤ x &lt; (s의 길이)) 칸만큼 회전시켰을 때 s가 올바른 괄호 문자열이 되게 하는 x의 개수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>s의 길이는 1 이상 1,000 이하입니다.</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;stack&gt;
#include &lt;algorithm&gt;

using namespace std;

bool isValid(const string&amp; s)
{
    stack&lt;char&gt; st;
    for(int i = 0; i &lt; s.size(); i++)
    {   
        if(st.empty())
        {
            st.push(s[i]);
        }
        else
        {
            if((s[i] == &#39;]&#39; &amp;&amp; st.top() == &#39;[&#39;) ||
               (s[i] == &#39;)&#39; &amp;&amp; st.top() == &#39;(&#39;) ||
               (s[i] == &#39;}&#39; &amp;&amp; st.top() == &#39;{&#39;))
                st.pop();
            else
                st.push(s[i]);
        }
    }

    return st.empty();
}

int solution(string s) {
    int answer = 0;
    int turn = 0;
    while(turn &lt; s.size())
    {
        ++turn;
        answer += isValid(s);
        rotate(s.begin(), s.begin() + 1, s.end());
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[연속 부분 수열 합의 개수]]></title>
            <link>https://velog.io/@chowon_/%EC%97%B0%EC%86%8D-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-%ED%95%A9%EC%9D%98-%EA%B0%9C%EC%88%98</link>
            <guid>https://velog.io/@chowon_/%EC%97%B0%EC%86%8D-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-%ED%95%A9%EC%9D%98-%EA%B0%9C%EC%88%98</guid>
            <pubDate>Mon, 02 Sep 2024 04:47:52 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>철호는 수열을 가지고 놀기 좋아합니다. 어느 날 철호는 어떤 자연수로 이루어진 원형 수열의 연속하는 부분 수열의 합으로 만들 수 있는 수가 모두 몇 가지인지 알아보고 싶어졌습니다. 원형 수열이란 일반적인 수열에서 처음과 끝이 연결된 형태의 수열을 말합니다. 예를 들어 수열 [7, 9, 1, 1, 4] 로 원형 수열을 만들면 다음과 같습니다.
그림.png
원형 수열은 처음과 끝이 연결되어 끊기는 부분이 없기 때문에 연속하는 부분 수열도 일반적인 수열보다 많아집니다.
원형 수열의 모든 원소 elements가 순서대로 주어질 때, 원형 수열의 연속 부분 수열 합으로 만들 수 있는 수의 개수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>3 ≤ elements의 길이 ≤ 1,000
1 ≤ elements의 원소 ≤ 1,000</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;set&gt;
#include &lt;algorithm&gt;

using namespace std;

int solution(vector&lt;int&gt; elements) {
    int size = elements.size();
    int cnt = 0;
    set&lt;int&gt; s;
    while(cnt &lt; size)
    {
        ++cnt;
        for(int i = 0; i &lt; size; i++)
        {
            int sum = 0;
            for(int j = 0; j &lt;= i; j++)
            {
                sum += elements[j];
            }

            s.insert(sum);
        }

        rotate(elements.begin(), elements.begin() + 1, elements.end());
    }

    return s.size();
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[귤 고르기]]></title>
            <link>https://velog.io/@chowon_/%EA%B7%A4-%EA%B3%A0%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@chowon_/%EA%B7%A4-%EA%B3%A0%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Mon, 02 Sep 2024 04:33:38 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>경화는 과수원에서 귤을 수확했습니다. 경화는 수확한 귤 중 &#39;k&#39;개를 골라 상자 하나에 담아 판매하려고 합니다. 그런데 수확한 귤의 크기가 일정하지 않아 보기에 좋지 않다고 생각한 경화는 귤을 크기별로 분류했을 때 서로 다른 종류의 수를 최소화하고 싶습니다.</p>
<p>예를 들어, 경화가 수확한 귤 8개의 크기가 [1, 3, 2, 5, 4, 5, 2, 3] 이라고 합시다. 경화가 귤 6개를 판매하고 싶다면, 크기가 1, 4인 귤을 제외한 여섯 개의 귤을 상자에 담으면, 귤의 크기의 종류가 2, 3, 5로 총 3가지가 되며 이때가 서로 다른 종류가 최소일 때입니다.</p>
<p>경화가 한 상자에 담으려는 귤의 개수 k와 귤의 크기를 담은 배열 tangerine이 매개변수로 주어집니다. 경화가 귤 k개를 고를 때 크기가 서로 다른 종류의 수의 최솟값을 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<p>1 ≤ k ≤ tangerine의 길이 ≤ 100,000
1 ≤ tangerine의 원소 ≤ 10,000,000</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;map&gt;
#include &lt;algorithm&gt;

using namespace std;

int solution(int k, vector&lt;int&gt; tangerine) {
    map&lt;int/*귤 크기*/, int/*귤 개수*/&gt; m;
    int size = tangerine.size();
    for(int i = 0; i &lt; size; i++)
    {
        m[tangerine[i]]++;
    }

    vector&lt;int&gt; v;
    for(auto it : m)
    {
        v.push_back(it.second);
    }

    sort(v.begin(), v.end(), greater&lt;int&gt;());

    int sum = 0;
    for(int i = 0; i &lt; size; i++)
    {
        sum += v[i];

        if(sum &gt;= k)
            return i + 1;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[예상 대진표]]></title>
            <link>https://velog.io/@chowon_/%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9C</link>
            <guid>https://velog.io/@chowon_/%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9C</guid>
            <pubDate>Mon, 02 Sep 2024 03:54:36 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>△△ 게임대회가 개최되었습니다. 이 대회는 N명이 참가하고, 토너먼트 형식으로 진행됩니다. N명의 참가자는 각각 1부터 N번을 차례대로 배정받습니다. 그리고, 1번↔2번, 3번↔4번, ... , N-1번↔N번의 참가자끼리 게임을 진행합니다. 각 게임에서 이긴 사람은 다음 라운드에 진출할 수 있습니다. 이때, 다음 라운드에 진출할 참가자의 번호는 다시 1번부터 N/2번을 차례대로 배정받습니다. 만약 1번↔2번 끼리 겨루는 게임에서 2번이 승리했다면 다음 라운드에서 1번을 부여받고, 3번↔4번에서 겨루는 게임에서 3번이 승리했다면 다음 라운드에서 2번을 부여받게 됩니다. 게임은 최종 한 명이 남을 때까지 진행됩니다.</p>
<p>이때, 처음 라운드에서 A번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 궁금해졌습니다. 게임 참가자 수 N, 참가자 번호 A, 경쟁자 번호 B가 함수 solution의 매개변수로 주어질 때, 처음 라운드에서 A번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 return 하는 solution 함수를 완성해 주세요. 단, A번 참가자와 B번 참가자는 서로 붙게 되기 전까지 항상 이긴다고 가정합니다.</p>
<h3 id="제한사항">제한사항</h3>
<p>N : 21 이상 220 이하인 자연수 (2의 지수 승으로 주어지므로 부전승은 발생하지 않습니다.)
A, B : N 이하인 자연수 (단, A ≠ B 입니다.)</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

int solution(int n, int a, int b)
{
    --a;
    --b;

    int answer = 0;
    while(a != b)
    {
        ++answer;
        a /= 2;
        b /= 2;
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[영어 끝말잇기]]></title>
            <link>https://velog.io/@chowon_/%EC%98%81%EC%96%B4-%EB%81%9D%EB%A7%90%EC%9E%87%EA%B8%B0</link>
            <guid>https://velog.io/@chowon_/%EC%98%81%EC%96%B4-%EB%81%9D%EB%A7%90%EC%9E%87%EA%B8%B0</guid>
            <pubDate>Sun, 01 Sep 2024 05:34:22 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>1부터 n까지 번호가 붙어있는 n명의 사람이 영어 끝말잇기를 하고 있습니다. 영어 끝말잇기는 다음과 같은 규칙으로 진행됩니다.</p>
<p>1번부터 번호 순서대로 한 사람씩 차례대로 단어를 말합니다.
마지막 사람이 단어를 말한 다음에는 다시 1번부터 시작합니다.
앞사람이 말한 단어의 마지막 문자로 시작하는 단어를 말해야 합니다.
이전에 등장했던 단어는 사용할 수 없습니다.
한 글자인 단어는 인정되지 않습니다.
다음은 3명이 끝말잇기를 하는 상황을 나타냅니다.</p>
<p>tank → kick → know → wheel → land → dream → mother → robot → tank</p>
<p>위 끝말잇기는 다음과 같이 진행됩니다.</p>
<p>1번 사람이 자신의 첫 번째 차례에 tank를 말합니다.
2번 사람이 자신의 첫 번째 차례에 kick을 말합니다.
3번 사람이 자신의 첫 번째 차례에 know를 말합니다.
1번 사람이 자신의 두 번째 차례에 wheel을 말합니다.
(계속 진행)
끝말잇기를 계속 진행해 나가다 보면, 3번 사람이 자신의 세 번째 차례에 말한 tank 라는 단어는 이전에 등장했던 단어이므로 탈락하게 됩니다.</p>
<p>사람의 수 n과 사람들이 순서대로 말한 단어 words 가 매개변수로 주어질 때, 가장 먼저 탈락하는 사람의 번호와 그 사람이 자신의 몇 번째 차례에 탈락하는지를 구해서 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<p>끝말잇기에 참여하는 사람의 수 n은 2 이상 10 이하의 자연수입니다.
words는 끝말잇기에 사용한 단어들이 순서대로 들어있는 배열이며, 길이는 n 이상 100 이하입니다.
단어의 길이는 2 이상 50 이하입니다.
모든 단어는 알파벳 소문자로만 이루어져 있습니다.
끝말잇기에 사용되는 단어의 뜻(의미)은 신경 쓰지 않으셔도 됩니다.
정답은 [ 번호, 차례 ] 형태로 return 해주세요.
만약 주어진 단어들로 탈락자가 생기지 않는다면, [0, 0]을 return 해주세요.</p>
<h3 id="풀이">풀이</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;set&gt;

using namespace std;

vector&lt;int&gt; solution(int n, vector&lt;string&gt; words) {
    vector&lt;int&gt; answer = {0, 0};
    set&lt;string&gt; s;
    s.insert(words[0]);

    for(int i = 1; i &lt; words.size(); i++)
    {
        if(words[i - 1].back() != words[i].front() || 
           s.insert(words[i]).second == false)
        {
            int temp = i + 1;
            int num = temp % n == 0 ? n : temp % n;
            int turn = (temp + n - 1) / n;

            answer[0] = num;
            answer[1] = turn;
            break;
        }
    }

    return answer;
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>