w99hyun
프로필프로젝트포스트연락처
목록
w99hyun

본 사이트는 개인 포트폴리오 게시용으로 제작되었습니다.

ⓒ 2026. w99hyun(Woohyun Cho) All rights reserved.


C++ Summary (vs. C#)

원본 글 보기

2025년 9월 9일


개인적으로 공부하면서, C#과 같이 사용하면서 헷갈릴 수 있는 부분(C++과 다른 점)과 C++에서 특정 버전 이상에서만 지원하는 문법을 정리한다.

C++ 버전별 주요 특징

Modern C++은 C++11 이후 버전을 의미한다.


typedef

typedef double my_type_t;
== using my_type_t = double; (C++11 이상)

enum

enum { };
enum class { }; (C++11 이상)

//c#의 enum과 같이 사용하려면 enum class를 사용한다.//enum은 스코프를 사용하지 않지만 enum class에서는 C#과 마찬가지로 스코프를 사용한다.

string

//c++의 string은 std 라이브러리에 포함된다.

//cin으로 문자열을 온전히 받기 위해선std::getline(std::cin, {변수명})//이 사용된다.
//cin으로 입력받은 버퍼를 비우기 위해std::cin.ignore(std::numeric_limitsstd::streamsize::max(), '\n');== std::cin.ignore(32767, '\n');//사용이 필요하다.

코드에서 긴급한 탈출 (halt)

HALT → exit(0);

랜덤 난수 생성

#include <cstdlib>

std::srand(5323); //5323 = seed → 시드 넘버 지정std::srand(static_cast<unsigned int>(std::time(0)); // 시간과 연동하여 시드가 계속 변경됨std::rand();
#include <random> (C++11 이상)
std::random_device rd;std::mt19937_64 mesenne(rd()); // or mt19937std::uniform_int_distribution<> dice(1 ,6); // 1~6까지 같은 확률

cin 활용

# 버퍼 지우기
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
== std::cin.ignore(32767, '\n');

입력 범위 문제 확인하기 (둘이 같이 사용됨.)
std::cin.fail(); // return true / falsestd::cin.clear(); // 내부 상태 플래그 초기화

배열과 포인터

배열을 정의하면 배열의 이름 자체가 주소가 된다.
다만 배열을 함수의 인자로 넘겨, 매개변수로 배열을 받는다면 이 매개변수는 '포인터'로 취급된다.
때문에 매개변수로 받은 배열의 이름(포인터)은 배열의 주소를 저장하는 다른 주소가 된다.
→ 함수에서 이 (배열로 보이지만 포인터 변수인)배열의 sizeof를 찍으면 4바이트(32비트, 64비트에서는 8바이트)로 출력된다. 


포인터를 그냥 선언해서는 변수값을 담을 수 없다.<blockquote>
char *name = "nolda"; (X)변수값을 담기 위해선 const 선언을 넣으면 사용할 수 있다.const char *name = "nolda"; (O)
</blockquote>


//참고(*this).memberValue;== this->memberValue;

foreach

C#의 foreach와 사용법은 비슷하지만 문법의 차이가 존재한다.

for (int value : array){}

메모리 할당

정적으로 할당된 메모리는 stack에 저장되며, stack은 용량이 작고 컴파일 타임에 크기가 결정된다.
동적으로 할당된 메모리는 Heap에 할당된다. Heap 영역은 런타임에 결정된다.

Stack / Heap

Memory의 Segment
1. Code - Program
2. BSS - uninitialized data
3. Data - initialized data
4. Stack
 - 로컬 변수, 메인 함수, 메소드 등이 스택으로 쌓임
 - 사이즈가 작기 때문에 Stack Overflow 발생 가능
5. Heap
 - 동적 할당일 경우 Heap 영역에 저장됨


const와 포인터

int value = 5;
const int *ptr1 = &value; //ptr1에 저장돼있는 주소를 바꾸는건 가능하지만, 주소가 가리키는 value 값을 바꾸는건 안됨
int *const ptr2 = &value; //ptr2에 저장돼있는 주소 값 바꾸는게 안됨
const int *const ptr3 = &value; //주소 값도 바꿀 수 없고 de-referencing으로 값을 바꿀 수도 없음 (다 안됨)

std::vector

#include <vector>
std::vector<int> array_value;

동적할당 배열에 유용하게 쓰이고 널리 쓰임. C#의 List와 비슷하다.//size : 사용하는 용량 -> .resize()//capacity : 총 용량(size에서 가려진 총 용량) -> .reserve
//vector를 stack처럼 사용하기.push_back();, .pop_back()

std::tuple

여러 개의 반환 값을 return 시킬 수 있다.

#include <tuple>
std::tuple<int, double> getTuple(){    return std::make_tuple(5, 3.14);}
int main(){    std::tuple<int, double> tp = getTuple();    std::get<0>(tp); //int 값    std::get<1>(tp); //double 값
//C++17 이상에서는 아래가 가능하다.
auto[a, b] = getTuple();

}

함수포인터

int func() { return 5; }
int func2() { return 9; }

//함수포인터 변수 선언int(*fcnptr)() = func;-> int(*변수이름)(매개변수)
//다른 함수 할당fcnptr = func2;
//functional Library (C++11 이상)#include <functional>
std::functional<리턴타입(매개변수)> fcnptr = func;

일립시스 (Ellipsis)

//매개변수의 갯수제한을 두지 않고 받는 방법

double findAverage(int count, ...) //count : 매개변수 갯수{    double sum = 0;    va_list list;    var_start(list, count);
for (int arg = 0; arg < count; ++arg)
{
    sum += va_arg(list, int);
}

var_end(list);

return sum / count;

}

연쇄호출(Chaining)

class 내부 함수의 리턴을 class 자신의 reference 값으로 설정하면,
연쇄적으로 호출이 가능하다.

class Calc{    int m_value;
Calc& Add(int value) { m_value += value; return *this; }
Calc& Sub(int value) { m_value -= value; return *this; }

};
int main(){    Calc cal(10);    cal.Add(10).Sub(20).Add(10);}

friends keyword

class에서 다른 함수의 선언부를 가져와 friend 선언을 해주면 해당 함수에서는 선언된 class의 private 멤버에 접근할 수 있다.

각자 다른 class에서 공통 함수에 friend를 선언할 경우 전방선언이 필요할 수 있음.
순서에 의해 friend 함수에서 멤버 변수 등을 인식하지 못하면, class에는 선언부만 남겨두고,인식하지 못한 class의 하단에 구현부를 넣어준다.

익명 변수

class A
{
    void print()
    {
        cout << "A Print" << endl;
    }
};

int main(){    A().print();    A().print(); //위의 객체와 다르기 때문에 생성자와 소멸자가 각각 호출됨.}

연산자 오버로딩

//산술 연산자
class Cents
{
private:
    int m_cents;
public:
    int& getCents() { return m_cents; }

Cents operator + (const Cents &c2) //멤버 함수
{
    return Cents(this->m_cents + c2.m_cents);
}

friend Cents operator + (const Cents &c1, const Cents &c2) //friend 함수
{
    return Cents(c1.getCents() + c2.getCents());
}

};
//입출력 연산자class Point{private:    double m_x, m_y, m_z;
public:    Point(double x = 0.0, double y = 0.0, double z = 0.0)        : m_x(x), m_y(y), m_z(z)    { }
//outstream
friend std::ostream& operator << (std::ostream &out, const Point &point)
{
    out <<<< point.m_x << ", " << point.m_y << ", " << point.m_z;

    return out;
}

//instream
friend std::istream& operator >> (std::istream& in, Point& point)
{
    in >> point.m_x >> point.m_y >> point.m_z;

    return in;
}

};
int main(){    Point p1(0.0, 0.1, 0.2), p2(3.4, 1.5, 2.0);    Point p3, p4;
cout << p1 << " " << p2 << endl;
cin >> p3 >> p4;

}
//단항 연산자Cents operator - () const{    return Cents(-m_cents);}
bool operator ! () const{    return (m_cents == 0 ? true : false);}
//비교 연산자
friend bool operator == (const Cents& c1, const Cents& c2){    return c1.m_cents == c2.m_cents;}
friend bool operator < (const Cents& c1, const Cents& c2){    return c1.m_cents < c2.m_cents;}
//증감 연산자Cents& operator ++ () //prefix{    ++m_cents;    return *this;}
Cents& operator ++ (int) //postfix{    Cents temp(m_cents);    ++(*this);    return *temp;}
//첨자 연산자 []class IntList{private:    int m_list[10];public:    int& operator [] (const int index)    {        return m_list[index];    }    const int& operator [] (const int index) const    {        return m_list[index];    }};
int main(){    IntList list;
list[3] = 10;

//if
IntList *list = new IntList;

list[3] = 10; //X
(*list)[3] = 10; //O

}
//형변환
operator int(){    return m_cents;}

explicit / delete

class Fraction
{
    int m_numerator;
    int m_denominator;

Fraction(char) = delete; //사용 못하게 막음

(explicit) Fraction(int num = 0, int den = 1)
    : m_numerator(num), m_denominator(den)
{ }

};void doSomething(Fraction frac){    cout << frac << endl;}
int main(){    doSomething(7); //원래는 컴파일러에서 Fraction(7)처럼 변환해줌                    //만약 Fraction 생성자에 explicit 키워드가 앞에 붙는다면 불가능해짐}

class 깊은 복사 주의점

//class의 멤버변수로 char *m_data = nullptr;가 정의되어 있을 때,
//이 class를 기본 복사 생성자를 통해 복사하면 새로운 class instance에도 같은 포인터 주소를 가리킨다.
//이 때, 새로운 class instance가 삭제되면, 소멸자에서 해당 포인터 변수가 가리키는 값을 지워버리게 되고,
//원본의 데이터까지 지워버리는 상황이 발생한다. (얕은 복사)
//이 때문에, 깊은 복사를 위해선 별도의 복사 생성자를 정의해줘야 한다.

MyString(const MyString &source) //복사 생성자 (깊은 복사){    m_length = source.m_length;
if (source.m_data != nullptr)
{
    m_data = new char[m_length];
    for (int i = 0; i < m_length; ++i)
        m_data[i] = source.m_data[i];
}
else
    m_data = nullptr;
}

}
//operator = (대입 연산자) 의 경우에도 비슷하게 정의해줄 수 있음.MyString& operator = (const MyString &source){    if (this == &source)        return *this;
delete[] m_data; //기존에 갖고있던 메모리 할당 해제

m_length = source.m_length;

if (source.m_data != nullptr)
{
    m_data = new char[m_length];
    for (int i = 0; i < m_length; ++i)
        m_data[i] = source.m_data[i];
}
else
    m_data = nullptr;
}

}
//std::string을 사용하면 필요 없는 일이다.

Initializer List 생성자

IntArray(unsigned length)
    : m_length(length)
{
    m_data = new int[length];
}

IntArray(const std::initializer_list<int> &list)    : IntArray(list.size()){    int count = 0;    for (auto & element : list)    {        m_data[count] = element;        ++count;    }}

상속 관련 Keyword

//virtual
상속 구조에서 부모 클래스의 메소드에 virtual 키워드를 사용시
자식 클래스의 객체를 부모 클래스의 포인터에 넣어서 호출해도
자식클래스의 메소드가 호출됨.
부모 클래스의 함수를 자식 클래스에서 오버라이딩한 것으로 인식


가상 소멸자부모 클래스 객체에 자식 클래스를 넣을 경우,자식 클래스의 동적 할당된 메모리를 지우기 위해 소멸자에도virtual 키워드를 붙여주면 자식 클래스의 소멸자도 실행됨.

//override상속 구조에서 자식 클래스의 함수 매개변수 끝에 override 키워드 작성시오버로딩이 아닌 오버라이드를 의도한것이라고 인식하게 하는 키워드
//finalfinal 키워드 사용시 자식 클래스에서 더 이상 오버라이딩할 수 없음

다이아몬드 상속 문제

A라는 부모클래스를 통해 B와 C클래스를 상속받아 생성할 때,
상속 접근제한자에 virtual을 붙여주지 않으면
B와 C클래스 각각 다른 A클래스를 상속받는 문제가 발생할 수 있음.

class B : virtual public A와 같이 상속받아야 함.

Object Slicing

부모 클래스로부터 상속받아 생성된 자식클래스에 새로운 변수가 있는데,
부모 클래스에 자식 클래스 인스턴스를 넣어버린 경우 데이터 슬라이싱 발생

std::vector를 사용하는 경우std::vector<std::reference_wrapper<부모 클래스>> vec;을 사용할 수 있다.

dynamic cast

Derived d1;
Base *base = &d1;

auto *base_to_d1 = dynamic_cast<Derived1*>(base);Base로 형변환 됐던 변수를 다시 Derived로 형변환
dynamic_cast의 경우 에러 체크를 통해 에러일 경우 nullptr 반환함. (안전한 형변환)static_cast는 에러 체크를 하지 않음.

Template

//함수 템플릿

template<typename T>T getMax(T x, T y){    return (x > y) ? x : y;}
//클래스 템플릿
<*.h>template<typename T>class MyArray{private:    int m_length;    T *m_data;
public:    MyArray(int length)    {        m_lenth = length;        m_data = new T [length];    }
void print();

}...
<*.cpp>template<typename T>void MyArray<T>::print(){    ...}
template void MyArray<char>;//템플릿 클래스의 멤버함수의 구현부를 cpp 파일로 옮길 경우explicit instantiation 필요

smart pointer: auto_ptr (Regacy)

std::auto_ptr<int> //c++98 ~ c++11까지 존재 c++17부터 제거

//auto_ptr의 구조template<typename T>class AutoPtr{    public:    AutoPtr(T* ptr = nullptr) : m_ptr(ptr)    { }
~AutoPtr()
{
    if (m_ptr != nullptr) delete m_ptr;
}

};

  • 이동(move semantics)의 의미 → auto_ptr의 변수는 복사하면 '이동'이 된다. A변수와 B변수가 존재할 때, B에 A를 대입하면 A는 nullptr을 갖게된다.
  • auto_ptr은 배열에 사용할 경우 배열 이름 즉, 배열[0]요소에 대해서만 delete를 하는 치명적인 단점을 갖고있다.

R-value Reference

int x = 5;
const int cx = 5;

//int &&rr1 = x; (X)//int &&rr2 = cx; (X)int &&rr3 = 5;

  • L Value Reference와 다르게 메모리가 할당되지 않은 값을 할당 가능 (곧 사라질 값들만 할당 가능)

std::move

AutoPtr<Resource> res2 = std::move(res1);
//res1이 R-value임을 인식시켜줌
//이렇게 처리한 경우 res1을 사용하지 않는다는 의미
template<class T>
void MySwap(T &a, T &b)
{
  //Copy constructor
  T tmp = a;
  a = b;
  b = tmp;

  //Move Semantics  T tmp { std::move(a) };  a = std::move(b);  b = std::move(tmp);}
//AutoPtr class에 Move Semantics를 정의했음
example)
vector<string> v;string str = "Hello";
v.push_back(str); //L-valuecout << std << endl; //Hellocout << v[0] << endl; //Hello
v.push_back(std::move(str)); //R-valuecout << str << endl; // 공백cout << v[0] << " " << v[1] << endl; // Hello Hello

std::unique_ptr / std::make_unique (주로 사용되는 smart pointer)

#include <memory>

std::unique_ptr<Resource> res(new Resource(10000));

  • 영역 밖을 벗어나 사용되지 않으면 자동으로 소멸됨
auto res1 = std::make_unique<Resource>(5); 
  • 권장되는 초기화 방식
res2 = res1; (x)
res2 = std::move(res1); (O)
  • unique pointer는 L-value 복사가 되지 않음
void doSomething(std::unique_ptr<Resource> res)
{
}

doSomething(std::unique_ptr<Resource>(new Resource(1000))); (X)
doSomething(std::make_unique<Resource>(1000)); (O)

  • 위의 방식을 쓰면 컴파일러에 따라 문제가 발생할 수 있음 (parameter에서 new X)

std::shared_ptr / std::make_shared

std::shared_ptr<Resource> ptr1(res);
...
{
    std::shared_ptr<Resource> ptr2(ptr1);
    std::shared_ptr<Resource> ptr2(res); //이렇게 사용하면 ptr1이 res의 소유권이 다른 데에도 있다는 것을 알 수가 없음
}
  • ptr2의 블럭을 벗어나도, ptr1은 존재한다.
auto ptr1 = std::make_shared<Resource>(3);

std::weak_ptr

  • class 내부에서 shared_ptr을 통해 서로를 참조시키면, memory leak이 남아있는 채로 종료됨 (순환 참조 문제)
  • weak_ptr은 단독으로 사용할 수 없고, lock()을 해서 shared_ptr로 return해줘야 한다.
const std::shared_ptr<Person> getPartner() const
{
    return m_partner.lock(); //std::weak_ptr<Person> m_partner;
}

STL(Standard Template Library)

  • CPP Reference

    https://cppreference.com/index.html

  • Algorithms, Containers, Functions, Iterators를 포함
- std::set<T> //집합. 내부 원소가 겹치면 무시
  set.insert("Hello");
  set.insert("World");
  set.insert("Hello");
> "Hello World"


std::multiset<T> // 중복 원소 허용 집합

std::map<key, value> // key값에 sort 돼있음.first // key 출력.second // value 출력

std::multimap<key, value> // 중복 키값 허용 mapmultimap.insert(std::pair('a', 10)); //Before c++14, pair<char, int>('a', 10)

std::stack.push // push adds a copy.emplace // constructs a new object

std::queue

std::priority_queue //sort 해주는 queue, 사용자 지정 클래스를 우선순위 큐로 만들면, 크기 비교 연산자 오버로딩을 해줘야함

STL Iterator (반복자)

vector<int> container;
for (int i = 0; i < 10; ++i)
    container.push_back(i);

vector<int>::const_iterator itr; //vector<int>::iterator itr;itr = container.begin();
while(itr != container.end()){    cout << *itr << " ";
++ itr;

}

  • 어떤 컨테이너든 같은 코드로 순회가 가능하기 때문에 사용한다. → vector를 list 또는 set등으로 바꿔도 바로 동작함.
for (auto itr = container.begin(); itr != container.end(); ++itr)
    cout << *itr << " ";
for (auto &e : container)
    cout << e << " ";
  • 모두 동일하게 동작한다.

std::string / std::wstring

  • wide-string : 글로벌 std::locale 사용시 주로 사용 (다양한 Unicode 지원)
//int 값을 string으로 변환 → 문자열로 처리됨
std::string my_str(std::to_string(4));

//string을 int값으로 변환int i = std::stoi(my_str);
//std::ostringstream (output) / std::istringstream (input)

  • string도 vector와 마찬가지로 길이와 용량이 다르다.
  • C와 다르게, string의 뒤에는 null값 ('\0')이 포함되지 않는다.
string.length()
string.size()
string.capacity() //용량
string.max_size() //최대 크기 

string.reserve(1000) //용량 확보 (최소 용량)
string my_str("abcdefg");
try{    my_str[100] = 'X'; → 예외처리 X    my_str.at(100) = 'X'; → 예외처리 O}catch { }
my_str.c_str() == .data() → C 스타일로 사용할경우 마지막에 null값 ('\0') 포함
.append() → string의 끝에 문자열 붙이기

istream (Input Stream)

#include <iomanip>

char buf[10]cin >> setw(5) >> buf; //최대 5글자만 받도록 해줌. <iomanip> include 필요

  • setw()으로 글자수 제한을 하면, cin 버퍼에 남아있기 때문에 계속 존재함.
  • cin이 빈칸을 구분자로 사용하지만, 빈칸까지 읽게하려면 cin.get(var)을 사용
  • cin.get()으로 읽은 후, cin.gcount()를 통해 몇글자를 읽었는지 확인 가능
  • cin.getline()은 라인 단위로 읽음. → 줄바꿈 문자까지 같이 읽어짐 ('\n')
string buf;
getline(cin, buf);
  • cin.ignore()는 한 글자를 무시한다.
  • cin.peek()은 버퍼에서 꺼내지 않고, 다음에 올 글자를 확인함
  • cin.unget()은 마지막에 읽은 문자를 버퍼로 다시 넣음
  • cin.putback()은 원하는 글자를 버퍼에 넣음

ostream (Output Stream)

  • cout.setf(std::ios::showpos)는 기호 (+, -)를 숫자 앞에 표시한다.
  • cout.unsetf()은 위의 플래그 삭제
  • cout.setf(std::ios::hex, std::ios::basefield)는 16진수로 출력 == cout << std::hex;
  • cout.setf(std::ios::uppercase)는 16진수의 영문자를 대문자로 표시
  • cout << std::boolalpha를 통해 bool 값 출력
  • cout << std::setprecision(3)은 소숫점 자릿수 설정
  • cout << std::fixed는 소숫점 자릿수 고정
  • cout << std::scientific은 부동 소수점 방식 표기법
  • cout << std::showpoint 소수점 '.' 표기
cout << std::setw(10) << std::left(right / internal) << -12345 << endl;
//출력 정렬
  • cout.fill("*") 빈 칸을 별로 채워줌

sstream (String Stream)

#include <sstream>

stringstream os;
os << "Hello World"; // 버퍼에 덧붙임os.str("Hello World");  //버퍼 치환
string str;str = os.str(); //os.str(""); 파라미터로 공백을 넣으면 치환됨os >> str;cout << str;

stream state

void printStates(const std::ios& stream)
{
    stream.good();
    stream.eof();
    stream.fail();
    stream.bad();
}
void printCharacterClassification(const int& i)
{
    bool(std::isalnum(i));
    bool(std::isblank(i));
    bool(std::isdigit(i));
    bool(std::islower(i));
    bool(std::isupper(i));
    // → return 값이 int이므로 bool로 캐스팅
}

Regular Expressions (정규 표현식)

#include <regex> // C++11 부터 지원

regex re("\d"); //digit 1개== regex re("[[:digit:]]{1}");regex re("\d+"); //1개 이상의 숫자regex re("[ab]"); //a, b만regex re("[A-Z]{1, 5}"); //1개 이상 5개 이하의 A-Z 문자regex re("([0-9]{1})([-]?)([0-9]{1,4})"); // 0-9 숫자 1개 + '-'이 있어도 되고 없어도됨 + 0-9 숫자 1개이상 4개 이하
//regex_match를 통해 매치되는지 판별string str;getline(cin, str);
if (std::regex_match(str, re))    cout << "match" << endl;else    cout << "No match" << endl;   
//매치되는 것만 출력auto begin = std::sregex_iterator(str.begin(), str.end(), re);auto end = std::sregex_iterator();
for (auto itr = begin; itr != end; ++itr){    std::smatch match = *itr;    cout << match.str() << " ";}cout << endl;

fstream (File Stream - 파일 입출력)

#include <fstream>
//ASCII code - outputstream

ofstream ofs("my_first_file.dat"); // ostream ofs("my_first_file.dat", ios::app) → append mode : 데이터를 추가//ofs.open("my_first_file.dat");
ofs << "File Detail" << endl;
//ofs.close() → 영역을 벗어나면 소멸자가 닫아줌. 수동으로 처리할 필요 X
//ASCII code - inputstream
ifstream ifs("my_first_file.dat");
while (ifs){    std::string str;    getline(ifs, str);
std::cout << str << endl;

}
//Binary code - outputstream
const unsigned num_data = 10;ofs.write((char*)&num_data, sizeof(num_data)); //데이터 개수 정의
for (int i = 0; i < num_data; ++i)    ofs.write((char*)&i, sizeof(i));
//Binary code - inputstream
unsigned num_data = 0;ifs.read((char*)&num_data, sizeof(num_data)); //데이터 개수 확인
for (unsigned i = 0; i < num_data; ++i){    int num;    ifs.read((char*)&num, sizeof(num));
std::cout << num << endl;

}

  • 임의 위치 접근
ifstream ifs("my_file.txt");

ifs.seekg(5); //5바이트 이동 후 읽기 시작ifs.seekg(5, ios::cur); //이전에 이동했던 위치에서 5바이트 더 이동 후 읽기 시작
ifs.seekg(0, ios::end); //끝에서 0번째 (마지막 위치)ifs.tellg(); //현재 위치

  • 파일 열고, 읽고 쓰기 한번에
    fstream iofs(filename);
    

iofs.seekg(5); cout << (char)iofs.get() << endl; //read

iofs.seekg(5); iofs.put('A'); //write


<hr/>

Lambda (람다 함수) : 익명 함수

auto func = [](const int& i) -> void { cdout << "Hello, World!!" << endl; };

{

string name = "JackJack";
<a href="">&</a> { std::cout << name << endl; } ();

}

//lambda의introducer인 []에 &을 넣으면, 밖에있는 것을 레퍼런스로 가져올 수 있음. == name


<hr/>

std::function

std::function<void(int)> func3 = func2;

std::function func4 = std::bind(func3, 456); //반환값 파라미터 bind

Object instance; auto f = std::bind(&Object::hello, &instance, std::placeholders::_1); //멤버함수를 instance에 바인딩 //파라미터가 1개이므로 _1, 늘어나면 매개변수 추가(_2, _3, ...)


<hr/>

함수에서 리턴값 여러개 반환하기 (C++17)

#include

auto my_func() { return tuple(123, 456, 789); }

int main() { auto [a, b, c, d] = my_func();

std::cout << a << " " << b << " " << c << " " << d << endl;

return 0;

}


<hr/>

std::thread - 멀티 스레딩 (C++11)

std::thread t1 = std::thread({

while (true)
{ }

});

t1.join(); //thread가 있는데, main이 끝나버릴 수 있으므로 t1이 끝날 때까지 대기해줌


- 스레드가 여러개 있다면, 동시에 실행된다.

mutex mtx; //mutual exclusion (상호 배제)

auto work_func = [](const string& name) { for (int i = 0; i < 5; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100));

    mtx.lock();
    cout << name << " " << std::this_thread::get_id() << " is working " << i << endl;
    mtx.unlock();
}

};

std::thread t1 = std::thread(work_func, "JackJack"); std::thread t2 = std::thread(work_func, "Dash");

t1.join(); t2.join();


<hr/>

Race Condition - std::atomic, std::scoped_loc

#include

//int shared_memory(0); → t1 스레드가 더하는 순간 t2가 가로채는 문제 발생할 수 있음. atomic shared_memory(0); //문제 해결

int main() { auto count_func = { for (int i = 0; i < 1000; ++i) { this_thread::sleep_for(chrono::milliseconds(1)); shared_memory++; // == shared_memory.fetch_add(1); } };

thread t1 = thread(count_func);
thread t2 = thread(count_func);

t1.join();t2.join();
cout << "After" << endl;cout << shared_memory << endl;

}

- 다른 스레드가 메모리를 가로채는 문제
- std::atomic뿐만 아니라 mutex를 사용할 수도 있음.
- atomic 남용시 성능 하락 우려 존재

...

std::lock_guard lock(mtx);
shared_memory++;
...

- lock 후 unlock을 할 수 없을 때도 있으므로, `std::lock_guard` 사용을 권장

...

std::scoped_lock lock(mtx);
...


- (C++17 이상) `std::scoped_lock` 권장

<hr/>

작업 기반 비동기 프로그래밍 (Task)

#include

int main() { { //multi-threading int result; std::thread t([&] {result = 1 + 2;} ); t.join(); std::cout << result << std::endl; }

{ //task-based parallelism
    auto fut = std::async([] {return 1 + 2;});
    std::cout << fut.get() << std::endl;
}

{ //future and promise    std::promise<int> prom;    auto fut = prom.get_future();
auto t = std::thread([](std::promise<int>&& prom)
{
    prom.set_value(1 + 2);
}, std::move(prom));

cout << fut.get() << endl;
t.join();

}

}

- async는 join()으로 기다리지 않아도 된다

<hr/>

std::forward - 완벽한 전달

#include

struct MyStruct {};

void func(MyStruct& s) { cout << "Pass by L-ref" << endl; }

void func(MyStruct&& s) { cout << "Pass by R-ref" << endl; }

//Template을 사용하면 L-value와 R-value 구분을 못한다. template void func_wrapper(T t) { func(t); }

↓ Perfect Forwarding

template void func_wrapper(T&& t) { func(std::forward(t)); }

int main() { MyStruct s;

func_wrapper(s);
func_wrapper(MyStruct());

//func(s);//func(MyStruct());

}


<hr/>

자료형 추론

- `auto` / `template<typename>`
    - auto는 변수의 const와 &, volatile를 모두 떼버린다.
    - `const auto& auto_crx2 = crx`, `volatile auto vavx = vs` 와 같이 선언해야 한다.

- `decltype` (== typeof)

typedef decltype(lhs ** rhs) product_type;

product_type prod2 = lhs * rhs;
== decltype(lhs * rhs) prod3 = lhs * rhs;

typedef decltype(x) x_type;

typedef decltype((x)) x_type; → &를 붙여줌
```

이전 글

유니티 맵 청크(Chunk) 시스템 (feat. 잉걸불)

2025년 11월 2일

다음 글

일학습병행 첨단산업아카데미 회고 (가상훈련콘텐츠SW개발_L5)

2025년 3월 1일