C++ 요약 치트 — 간결한 참고서
C++17/20 문법, 표준 라이브러리, 클래스, 템플릿과 가장 자주 쓰는 관용구를 다룬 요약 치트시트로, 일상적인 상황의 약 80%를 커버한다.
C++ C++17 / C++20
ISO C++ (GCC / Clang / MSVC) · 다중 패러다임 (OOP · 제네릭 · 함수형) · 정적 · 강타입 · 공칭 타입
추천 학습 경로
먼저 「Hello World와 빌드 환경」으로 컴파일 흐름을 익히고 → 변수, 타입, 제어 흐름과 함수에 익숙해지고 → 표준 라이브러리 컨테이너(vector/map)와 문자열을 사용하고 → RAII와 스마트 포인터(mem 절)를 이해한 뒤 → OOP와 템플릿을 보고 → 마지막으로 필요할 때 스레드, 네트워크, 정규식, 빌드와 디버깅을 찾아보라. FAQ 절은 돌아와서 실수를 피할 때 유용하다.
1.Hello World와 빌드 환경
처음부터 C++ 프로그램을 컴파일, 실행, 구성하기: 툴체인, 명령행 인수, 다중 파일, 그리고 C++20 모듈.
최소 프로그램
모든 C++ 프로그램은 main()에서 실행을 시작하며, 0을 반환하면 성공을 의미합니다. std::cout는 표준 출력 스트림이고, <<로 내용을 보냅니다.
컴파일과 실행
g++ 또는 clang++로 컴파일합니다: -std는 C++ 표준 버전을 지정하고, -Wall -Wextra는 경고를 활성화하며, -o는 출력 파일 이름을 지정하고, ./app으로 실행합니다.
CMake 빌드
CMake는 크로스 플랫폼 주류 빌드 시스템입니다: CMakeLists.txt는 프로젝트와 타깃을 선언하고, -B는 빌드 디렉터리를 지정하며, --build는 컴파일하고, 결과물은 build/ 아래에 있습니다.
명령행 인수
main의 argc는 인수 개수이고, argv는 인수 문자열 배열이며, argv[0]은 항상 프로그램 이름입니다. 프로그램은 argv[1]부터 사용자 인수를 읽습니다.
종료 코드
main의 반환값은 운영 체제에 프로세스 종료 코드로 전달됩니다: 0은 성공을 의미하고, 0이 아닌 값은 오류 범주를 나타냅니다. EXIT_SUCCESS / EXIT_FAILURE를 사용하면 더 읽기 쉽습니다.
다중 파일 컴파일
선언은 헤더 파일에 (#pragma once로 중복 포함 방지), 정의는 .cpp에 두고, 사용할 곳에서 #include합니다. 컴파일 시 각 .cpp를 함께 컴파일러에 전달하여 링크합니다.
C++20 모듈
모듈은 헤더 파일을 대체합니다: export module은 모듈을 선언하고, export는 심볼을 내보내며, import로 가져옵니다. 컴파일이 더 빠르고 매크로 오염이 없지만, 여전히 보급기에 있습니다.
네임스페이스
네임스페이스는 심볼을 구성하여 충돌을 피합니다. 네임스페이스 한정(geo::area)으로 접근합니다; using namespace는 한 번에 모든 심볼을 가져와 모호성을 만들기 쉽기 때문에, using std::cout처럼 정확히 가져오는 것이 우선입니다.
2.변수와 상수
선언과 타입 추론, const/constexpr, 참조, 구조화 바인딩, 스코프, 그리고 C++ 스타일 타입 변환.
auto 타입 추론
auto는 초기화 식별자에 따라 타입을 추론하며, 즉시 초기화해야 합니다. 추론 시 참조와 최상위 const가 제거되므로, 필요할 때는 const auto&를 명시적으로 작성합니다.
const와 constexpr
const는 값이 불변임을 나타내고; constexpr은 컴파일 시 평가됨을 보장하여 배열 크기와 템플릿 인수에 사용할 수 있습니다. C++20 이후 constexpr 함수는 더 많은 로직을 포함할 수 있습니다.
참조
참조는 기존 객체의 별칭입니다: 선언 시 초기화해야 하며, 영원히 같은 객체를 가리킵니다. 인수로 참조를 전달하면 복사를 피하고 원본 값을 수정할 수 있습니다.
구조화 바인딩
C++17은 pair/tuple/구조체/배열을 명명된 변수로 풀어냅니다. map 반복에서 key/value를 풀어내는 것이 가장 흔한 사용 사례입니다.
인라인 변수
C++17 이전에는 헤더 파일의 비-constexpr 전역 변수가 중복 정의 오류를 일으켰습니다; inline 변수는 여러 컴파일 단위가 같은 객체를 공유할 수 있게 합니다.
mutable 멤버
const 멤버 함수에서도 mutable 멤버를 수정할 수 있으며, 캐시, 호출 횟수, 뮤텍스 등 「논리적으로 객체를 변경하지 않는」 상태에 전형적으로 사용됩니다.
decltype
decltype은 식의 선언 타입을 가져옵니다. decltype((x))은 괄호를 추가하면 참조로 추론되며, 흔한 함정입니다; 후행 반환 타입은 템플릿 함수에서 자주 사용됩니다.
스코프와 가려짐
C++는 블록 스코프에서 같은 이름의 바깥 변수를 가리는 것을 지원합니다. 전역 이름 가리기를 피하세요 —— ::n으로 명시적으로 전역에 접근할 수 있지만 가독성이 떨어집니다.
리터럴 접미사
접미사는 리터럴 타입을 지정합니다: LL은 long long, f는 부동소수점, s는 std::string으로 변환합니다. 0b 이진 접두사와 ' 숫자 구분 기호는 가독성을 높입니다.
C++ 스타일 변환
static_cast는 컴파일 시 의미 변환; dynamic_cast는 런타임 다형성 변환(포인터 실패 시 nullptr 반환, 참조는 bad_cast 던짐); const_cast로 const를 제거할 때는 원본 객체가 const가 아님을 확인해야 합니다.
3.데이터 타입
내장 타입, 열거형, 구조체, 템플릿 타입, 그리고 std::optional/variant/any 등 현대 도구 타입.
내장 타입
내장 타입은 크기와 부호 존재 여부로 구분됩니다. <cstdint>는 고정 폭 타입 int32_t/uint64_t를 제공하여 플랫폼 간 동작이 일관됩니다.
열거 클래스
enum class는 bare enum보다 안전합니다: 값은 스코프로 한정해서 접근해야 하고 int로 암시적 변환되지 않으며, 이름 누출과 정수 오용을 피합니다.
구조체
struct는 기본적으로 멤버가 공개되며, 동작 없는 데이터를 묶는 데 적합합니다. 기본값이 있는 멤버는 중괄호 집계 초기화로 한 줄에 객체를 만들 수 있습니다.
공용체
union은 여러 멤버가 같은 메모리를 공유하고 한 번에 하나만 저장합니다. 표준 C++에서는 std::variant가 더 안전하며, union은 일반적으로 저수준 메모리 재사용에만 사용됩니다.
템플릿 타입
템플릿은 타입을 매개변수화하고, 컴파일러가 사용된 타입에 따라 해당 인스턴스를 생성하며, 이는 제네릭 프로그래밍과 표준 라이브러리 컨테이너의 기초입니다.
타입 별칭
using으로 타입 별칭을 선언하는 것이 typedef보다 명확하며 템플릿 별칭을 정의할 수 있습니다. 별칭은 새 타입을 만들지 않고 같은 타입의 다른 이름일 뿐입니다.
optional 옵션 값
std::optional은 「값이 있을 수도 없을 수도」있는 반환값을 표현하며, 센티넬 값이나 out 인수를 대체합니다. 컨텍스트의 bool 판정으로 존재 여부를 확인하고, *로 내용을 가져옵니다.
variant 타입 유니온
std::variant는 타입 안전한 유니온으로, 어느 순간이든 타입 목록 중 하나를 보유합니다. std::get은 지정 타입을 가져오고(불일치 시 bad_variant_access 던짐), std::get_if는 포인터를 반환하며 예외를 던지지 않습니다.
any 임의 타입
std::any는 임의의 타입을 저장하고 런타임에 다시 가져올 수 있습니다. 이종 컨테이너나 플러그인 인수 전달에 적합하지만 타입 소거 오버헤드가 있어, 가급적 variant를 사용하세요.
pair와 tuple
pair는 두 값을, tuple은 임의 개수를 저장합니다. 구조화 바인딩은 요소를 명명된 변수로 풀어내 std::get으로 인덱스별 접근보다 읽기 쉽습니다.
4.포인터와 배열
베어 포인터, 스마트 포인터, 배열, 그리고 참조 —— 누가 메모리를 소유하고, 언제 어느 것을 쓰는가.
베어 포인터
베어 포인터는 객체 주소를 저장하며, *는 역참조, &는 주소 가져오기입니다. 베어 포인터는 메모리를 소유하지 않으므로, new/delete를 �지우거나 스마트 포인터에 위임해야 합니다.
포인터와 배열
배열 이름은 식에서 첫 요소를 가리키는 포인터로 퇴화하며, p+i는 주소 오프셋, *(p+i)는 값 가져오기입니다. 이는 C에서 상속된 저수준 동작입니다.
nullptr 널 포인터
nullptr은 타입 안전한 널 포인터 상수이며, NULL이나 0을 대체합니다. 널 포인터 역참조는 정의되지 않은 동작이므로 사용 전 반드시 널 여부를 확인해야 합니다.
new와 delete
new는 힙 메모리를 할당하여 포인터를 반환하고, delete가 해제합니다; new[]는 반드시 delete[]와 짝을 이루어야 합니다. 수동 관리는 누수가 쉬워 RAII 컨테이너나 스마트 포인터를 우선하세요.
unique_ptr 독점 포인터
unique_ptr은 소유권을 독점하며 스코프를 벗어나면 자동으로 delete됩니다. make_unique는 예외 안전; 소유권 이전은 std::move를 사용하며 복제는 금지됩니다.
shared_ptr 공유 포인터
shared_ptr은 참조 카운트로 소유권을 공유하며, 카운트가 0이 되어야 해제됩니다. 순환 참조로 인한 누수를 피하세요 —— weak_ptr로 순환을 끊습니다.
weak_ptr 약한 참조
weak_ptr은 객체를 소유하지 않고 카운트에도 영향을 주지 않으며, lock()으로 일시적으로 shared_ptr로 승격됩니다(객체가 이미 해제되면 빈 값 반환). 순환 참조를 끊는 데 전용됩니다.
참조 vs 포인터
참조는 문법이 더 직접적이고 널이 아님이 보장되므로, 인수로는 참조를 우선 사용; 「객체가 없을 수도」있거나 「다른 곳을 다시 가리켜야」할 때는 포인터를 사용합니다.
const 포인터
읽기는 오른쪽에서 왼쪽으로: int* const는 포인터 자체가 불변, const int*는 가리키는 대상이 불변. 혼용이 가장 흔한 선언 혼동 지점입니다.
void* 포인터
void*는 「알 수 없는 타입 메모리를 가리키는」 포인터로, 역참조하려면 구체적 타입으로 명시적 변환해야 합니다. C 스타일 API가 임의 데이터를 전달하는 데 사용하지만, C++에서는 가급적 피하세요.
5.제어 흐름
if/for/while/switch, 그리고 C++17 이후의 if/switch 초기화 문.
if / else
if/else는 조건에 따라 실행 경로를 선택합니다. C++17 이후의 if는 초기화 문을 가질 수 있습니다(if-init 참조).
for 반복
고전 for는 초기화, 조건, 증감 세 부분으로 구성됩니다. 카운트 반복은 ++i를 우선 사용하며, 의미적으로 임시 복사 한 번을 줄입니다.
범위 기반 for
범위 기반 for(C++11)는 컨테이너 요소를 순회하여 손으로 인덱스와 경계 오류를 작성하는 것을 피합니다. 요소를 수정하려면 auto&, 읽기 전용은 const auto&를 사용합니다.
while / do-while
while은 조건을 먼저 판정한 후 본문에 들어가고; do-while은 최소 한 번은 실행합니다. 반복 횟수를 모르고 런타임 조건에 의존하는 시나리오에 적합합니다.
switch / case
switch는 정수/열거 값별로 분배하며 if 사슬보다 명확합니다. 각 case는 반드시 break(또는 return)해야 하고, 그렇지 않으면 다음 분기로 떨어집니다.
break와 continue
continue는 이번 라운드의 남은 코드를 건너뛰고 다음 라운드로 들어가고; break는 즉시 전체 반복을 종료합니다. 이중 반복에서는 레이블로 바깥쪽으로 점프할 수 있습니다.
if 초기화 문
C++17 이후 if/switch는 초기화 변수를 선언할 수 있고, 스코프가 분기 안으로 제한되어 변수가 바깥으로 새는 것을 피하며, find+check의 관용적 작성법입니다.
삼항 연산자
삼항 연산자는 if/else 할당을 한 줄로 표현한다. 두 분기의 타입은 서로 호환되어야 하며, 로직이 복잡하면 if를 쓰는 편이 더 읽기 쉽다.
goto 레이블
goto는 레이블 줄로 즉시 점프하여 구조적 제어 흐름을 깨뜨리며, 거의 항상 flag나 return으로 대체할 수 있다. 유일하게 합리적인 사용처는 다중 중첩 루프를 한 번에 빠져나올 때뿐이다.
6.함수와 Lambda
선언과 정의, 오버로딩, 인자 전달, 람다와 함수 템플릿, 가변 인자 템플릿과 재귀.
선언과 정의
선언은 컴파일러에게 시그니처를 알리고, 정의는 구현을 제공한다. 선언은 헤더 파일에 두어 여러 번 포함되어도 오류가 없으며, 정의는 프로그램 전체에서 단 한 번만 존재해야 한다.
함수 오버로딩
함수 오버로딩은 같은 이름의 함수를 인자 타입에 따라 분기 처리하도록 하며, 컴파일러는 가장 일치하는 버전을 선택한다. 반환 타입만으로 오버로딩을 구분할 수는 없다.
기본 매개변수
기본 매개변수는 호출 측에서 꼬리 인자를 생략할 수 있게 한다. 기본값은 가장 오른쪽부터 연속해서만 제공할 수 있으며, 선언과 정의 중 한 곳에서만 지정해야 한다.
인자 전달 방식
값으로 전달하면 객체 전체가 복사되고, const&로 전달하면 복사 없이 변경도 불가하며, &로 전달하면 원본을 수정할 수 있다. &&(rvalue 참조)는 임시 객체를 받아 자원을 이전하기 위한 것이다.
반환값
모던 C++에서는 반환값 최적화(RVO/NRVO)로 임시 값을 반환할 때 복사가 발생하지 않는다. 지역 객체의 주소나 참조를 절대 반환해서는 안 된다(댕글링).
람다 표현식
람다는 이름 없는 함수 객체로, [] 캡처 목록 + 매개변수 + 함수 본문으로 구성된다. <algorithm>과 함께 쓰면 콜백이나 비교 함수를 작성하는 주된 방법이 된다.
람다 캡처
[=]은 모든 변수를 값으로 캡처하고, [&]는 모든 변수를 참조로 캡처하며, [x]는 지정한 변수만 캡처한다. 참조로 캡처할 때는 람다가 살아 있는 동안 원본 변수가 유효해야 한다.
함수 템플릿
함수 템플릿은 호출 인자에서 타입을 추론하여 인스턴스를 생성하며, 하나의 구현으로 모든 타입을 처리한다. 사용된 연산자는 해당 타입이 반드시 지원해야 한다.
가변 인자 템플릿
매개변수 팩 ...Args는 임의 개수의 인자를 받고, 폴드 표현식 (args + ...)은 이항 연산자로 이를 펼쳐 printf 스타일의 사용자 정의 함수를 현대적으로 대체한다.
재귀
재귀는 함수가 자기 자신을 호출하는 것이며, 기저 조건이 재귀를 종료한다. 깊은 재귀는 호출 스택을 많이 소모하므로 스택 오버플로와 중복 계산의 최적화에 주의한다.
7.문자열
std::string 기본, 연결, 검색과 치환, 포맷팅, 문자열 스트림, 수치와의 상호 변환.
std::string 기본
std::string은 변경과 확장이 가능한 문자열 컨테이너로, 메모리를 자동으로 관리한다. [] 첨자 접근의 범위 초과는 미정의 동작이며, at()은 예외를 던진다.
문자열 리터럴
"..." 리터럴은 기본적으로 const char*이며, s 접미사를 붙이면 std::string이 된다. string_view는 읽기 전용 비소유 뷰로, 매개변수로 쓰면 복사를 피할 수 있다.
연결과 사전 할당
대량의 연결에는 reserve로 미리 할당해 메모리 재할당을 줄인다. 여러 번의 +는 임시 객체를 생성하므로 핫스팟 코드에서는 버퍼 재사용에 주의한다.
부분 문자열과 검색
substr(pos, len)은 부분 문자열을 추출하고, find/rfind는 처음으로 일치하는 위치를 찾으며, 찾지 못하면 std::string::npos를 반환한다.
치환
멤버 함수 replace(pos, len, str)은 위치 기반 치환을 하고, <algorithm>의 std::replace는 값 기반으로 일치하는 모든 문자를 일괄 치환한다.
포맷팅 출력
std::format(C++20)은 자리표시자를 사용해 포맷팅하며, 인자 개수를 컴파일 타임에 검사해 sprintf와 cout 연결을 대체한다. :04d는 0으로 채우고, :.2f는 소수점 둘째 자리까지 남긴다.
문자열 스트림
stringstream은 메모리 상의 문자열을 스트림처럼 다룬다. ostringstream은 연결용, istringstream은 >>로 파싱하는 용도로 사용하며, 포맷팅과 역파싱의 유연한 도구이다.
문자열과 수치의 상호 변환
stoi/stod/stoll은 문자열을 수치로 파싱하고, std::to_string은 그 역방향 변환을 수행한다. 파싱 실패나 오버플로 시 std::invalid_argument/out_of_range 예외를 던진다.
원시 문자열
R"(...)" 원시 문자열은 역슬래시를 이스케이프하지 않으므로 정규식, 경로, 여러 줄 텍스트를 이스케이프 지옥 없이 작성할 수 있다. 내용에 )"가 포함되면 R"tag(...)tag" 형식을 사용한다.
문자 단위 순회
범위 기반 for로 문자를 순회하는 것이 가장 간결하며, 수정이 필요하면 char&를 사용한다. begin/end 이터레이터 방식은 기존 코드와 알고리즘 라이브러리와의 호환을 위해 쓰인다.
8.컨테이너와 알고리즘
vector, array, map, set, 우선순위 큐와 정렬, 탐색, ranges 알고리즘.
vector 동적 배열
vector는 동적 배열로, 끝에서의 삽입과 삭제는 O(1), 임의 접근은 O(1)이다. push_back이 잦다면 reserve로 용량을 미리 확보하고, 앞쪽 삽입(O(n))은 피한다.
std::array 고정 길이 배열
std::array는 스택에 놓이는 고정 길이 배열의 모던 래퍼로, size()/begin()/end()를 갖추고 알고리즘 라이브러리에 참여할 수 있다. 크기가 고정이라면 array, 가변이면 vector를 쓴다.
map 정렬된 사전
std::map은 키로 정렬되는 정렬된 키-값 쌍(레드-블랙 트리)이며, 검색은 O(log n)이다. 정렬이 필요 없다면 unordered_map(평균 O(1))을 사용한다.
unordered_map 해시 테이블
unordered_map은 해시 테이블로, 평균 O(1)의 검색을 제공하지만 순서가 없다. operator[]는 존재하지 않는 키에 대해 0 값을 삽입하므로, 단순 조회에는 find나 at()을 사용한다.
set 집합
set은 정렬된 유일 원소 집합을 저장하며, insert/erase/find 모두 O(log n)이다. unordered_set은 해시 버전(순서 없음, O(1))이고, contains()(C++20)로 존재 여부를 판별한다.
deque와 list
deque는 양쪽 끝에서 O(1)이고, list는 중간 삽입이 O(1)이지만 임의 접근은 O(n)이다. 접근 패턴에 맞춰 컨테이너를 선택한다.
우선순위 큐(힙)
priority_queue는 힙으로, push는 O(log n), top으로 최댓값을 보는 것은 O(1)이다. 기본은 최대 힙이며, 최소 힙을 만들려면 greater 비교자를 지정해야 한다.
정렬
std::sort는 임의 접근 컨테이너를 제자리로 정렬하며 O(n log n)이다. 사용자 정의 비교자는 "a가 b보다 앞에 오는가"를 반환한다. list는 멤버 sort()를 사용한다.
검색
정렬되지 않은 집합은 std::find로 선형 검색하고, 이미 정렬된 컨테이너는 binary_search/lower_bound로 이분 검색 O(log n)을 수행한다. find는 이터레이터를 반환하므로 != end()로 적중 여부를 판단한다.
ranges 파이프 뷰
C++20 ranges는 | 파이프로 지연 뷰를 조합한다. filter로 걸러내고 transform으로 매핑하며, 복사도 할당도 없이 표현식 스타일로 시퀀스를 처리한다.
9.동적 메모리와 소유권
RAII, 이동 의미론, 스마트 포인터의 선택과 예외 안전성 — C++ 자원 관리의 핵심.
RAII(자원 획득이 초기화)
RAII는 자원 획득을 생성자에, 해제를 소멸자에 두어 객체가 파괴될 때 자동으로 해제되도록 한다. 예외 안전하고 명시적인 정리가 필요 없다.
make_unique
make_unique는 unique_ptr을 생성하며 new와 생성을 한 번에 처리하므로, 생성 중 예외가 발생해도 원시 포인터가 누수되지 않는다. 배열 버전은 make_unique<T[]>(n).
make_shared
make_shared는 객체와 제어 블록을 한 번에 할당하여, new로 생성해 shared_ptr로 감싸는 것보다 할당 횟수가 한 번 적다. C++20부터는 제어 블록이 객체 크기 정렬을 낭비하지 않는다.
이동 의미론
std::move는 좌측값을 우측값 참조로 변환하여 복사 대신 이동을 발동시킨다. 내부 포인터를 이전하고 원본 객체를 비워 큰 배열의 깊은 복사를 피한다.
이동 생성
이동 생성은 원본 객체의 자원을 가져와 원본을 비우므로 복사보다 빠르다. noexcept로 선언하면 vector가 확장할 때 복사 대신 이동을 선택할 수 있다.
반환값 최적화
컴파일러가 불필요한 복사/이동 생성을 생략하여 반환값을 대상에 직접 구성한다. C++17부터 값으로 반환되는 임시 객체의 0 복사를 보장한다.
순환 참조 깨기
부모-자식 트리에서 부모는 shared_ptr로, 자식이 부모를 가리킬 때는 weak_ptr을 쓴다. 그렇지 않으면 서로를 소유하여 영원히 해제되지 않는다. lock()으로 임시 승격하여 객체 생존을 확인한다.
noexcept 약속
noexcept는 함수가 예외를 던지지 않음을 선언하며, vector의 확장 시 이동 생성을 선택하는 근거가 된다. 약속한 후 실제로 던지면 std::terminate로 프로그램이 종료되므로 확신이 없으면 표기하지 않는다.
수동 관리의 누수
수동 new/delete는 중간에 예외가 발생하거나 조기 return이 일어나면 누수를 일으킨다. RAII(스마트 포인터/컨테이너)는 정리를 소멸자에 맡겨 예외 안전하다.
10.객체 지향
클래스, 접근 제어, 생성과 소멸, 상속과 다형성, 순수 가상 인터페이스, friend와 연산자 오버로딩.
클래스 정의
class는 데이터와 연산을 함께 캡슐화한다. 멤버는 기본적으로 비공개이며(struct와 반대), 공개 메서드를 통해 비공개 데이터에 접근한다.
접근 지정자
public은 공개, protected는 파생 클래스에서 접근 가능, private는 본인만 접근 가능. 기본값은 class는 private, struct는 public이다.
생성자
생성자는 클래스명과 같으며, 초기화 목록 : x(x_)로 멤버를 초기화한다(함수 본문에서 대입하는 것보다 효율적). 인자가 없는 버전을 기본 생성자라 한다.
위임 생성
위임 생성은 한 생성자가 같은 클래스의 다른 생성자를 호출하여 초기화 로직의 중복을 피한다. 위임 대상은 반드시 이미 선언되어 있어야 한다.
소멸자
소멸자는 객체가 파괴될 때 실행되며, RAII에서 자원을 해제하는 위치다. 기본 클래스의 소멸자는 반드시 virtual이어야 하며, 그렇지 않으면 파생 클래스를 delete할 때 기본 클래스 부분만 소멸된다.
상속
상속은 파생 클래스가 기본 클래스의 인터페이스와 구현을 재사용하도록 한다. 기본 클래스 포인터/참조로 virtual 함수를 호출하면 런타임 다형성 디스패치가 발생한다.
가상 함수와 추상 클래스
virtual은 재정의 가능한 함수를 선언하고, 순수 가상(=0)은 해당 클래스를 추상 클래스로 만들어 인스턴스화할 수 없게 한다. virtual을 포함하는 클래스의 소멸자도 virtual이어야 한다.
override와 final
override는 "기본 클래스의 가상 함수를 재정의하고 있음"을 선언하여, 시그니처 불일치 시 조용히 새 함수가 만들어지지 않고 컴파일 오류가 나도록 한다; final은 추가 재정의를 금지한다.
순수 가상 인터페이스
순수 가상 함수 = 0은 구현을 제공하지 않고 인터페이스만 정의하며, 순수 가상을 포함한 클래스는 추상 클래스이고, 인스턴스화하려면 파생 클래스가 반드시 구현해야 한다. Java의 interface와 비슷하다.
프렌드
friend는 지정한 클래스나 함수가 private 멤버에 접근하도록 허용해 캡슐화를 깨뜨린다. 강하게 결합된 상황(연산자 오버로딩, 내부 이터레이터 등)에 신중히 사용해야 한다.
연산자 오버로딩
연산자 오버로딩은 사용자 정의 타입이 + - << 등의 문법을 지원하도록 한다. 직관적인 의미를 유지한다: +는 피연산자를 수정하지 않고, <<는 출력이다. 남용하면 가독성이 떨어진다.
11.오류 처리
예외 잡기, 표준 예외 계층, 사용자 정의 예외, noexcept와 optional/expected를 통한 예외 없는 경로.
try / catch
try 블록 안에서 던진 예외는 catch가 잡는다. 기본적으로 std::exception& 베이스로 잡고, 필요하면 구체적인 타입별로 catch를 나눈다; 잡지 않으면 호출 스택을 따라 전파된다.
참조로 잡기
const&로 잡으면 다형 타입을 보존하고 복사본이 없다; 값으로 잡으면 슬라이스된다(파생 예외가 기본 클래스로 잘림). catch(...)는 모든 것을 잡지만 정보를 얻을 수 없다.
표준 예외 계층
표준 예외는 std::exception에서 상속한다. 구체적인 타입 분기를 앞에 두고, std::exception&를 마지막에 두어 가장 구체적인 것을 우선 매칭한다.
사용자 정의 예외
사용자 정의 예외는 std::runtime_error(또는 그 형제 클래스)에서 상속하고, using으로 생성자를 상속하며, what()이 이미 메시지를 담고 있으므로 잡을 때는 베이스 클래스로 잡으면 된다.
noexcept와 예외
noexcept는 예외를 던지지 않겠다는 약속이며, vector의 확장, move가 이에 의존한다. 실제로 던지면 std::terminate로 프로그램이 즉시 종료되므로, 확신이 없으면 쓰지 않는다.
optional로 실패 표현
"실패할 수 있다"고 예상되는 연산은 std::optional을 반환하는 것이 예외보다 가볍고, 호출 측에서 값이 없는 경우를 명시적으로 처리한다. 예외는 예기치 못한 오류에 남겨둔다.
expected로 오류 전달
std::expected(C++23)는 값과 오류 설명을 동시에 담으며, optional보다 더 많은 오류 정보를 전달하고 예외보다 예측 가능해, 파싱·검증 류 코드에 적합하다.
예외 안전성
예외 안전성은 RAII에 의존한다: 정리 작업을 로컬 객체의 소멸자에 넣으면, 정상 반환이든 예외 던짐이든 모두 실행되어 리소스 누수와 상태 불일치를 막는다.
errno와 C 상호 운용
errno는 C 시대의 전역 오류 코드로, 다음 실패 호출에 덮어씌워지며 스레드 안전하지 않다. C++은 예외를 우선 사용하고, errno는 C 함수와 상호 운용할 때 즉시 읽는다.
12.파일과 스트림 I/O
파일 읽기/쓰기, 줄 단위 읽기, 형식 있는 출력, filesystem 경로와 바이너리 파일.
파일 쓰기
ofstream으로 파일을 열어 쓰고, <<로 형식 있는 출력을 한다. 소멸 시 자동으로 닫힌다; 명시적인 close()는 쓰기 실패를 더 일찍 잡을 수 있게 한다. 기본적으로 기존 파일을 덮어쓴다.
파일 읽기
ifstream으로 파일을 열어 읽고, getline으로 줄 단위로 읽는다. 루프 조건은 getline의 반환값이다: EOF나 실패에 도달하면 끝난다.
줄 읽기와 단어 읽기
getline은 한 줄 전체를 읽고, >>는 공백을 건너뛰며 한 단어를 읽는다. 섞어 쓸 때 >> 이후 남은 개행은 ignore()로 비워야 하며, 그렇지 않으면 getline이 빈 문자열을 읽는다.
형식 제어
<iomanip>의 setw/setprecision/fixed가 출력 형식을 제어하며, 표를 출력할 때 열 너비 정렬과 소수 자릿수 제어에 자주 쓰인다.
메모리 스트림으로 디스크 쓰기
ostringstream으로 메모리에서 먼저 조립한 뒤 한 번에 파일에 쓰면 디스크 I/O를 여러 번 하지 않는다; 입력을 파싱할 때는 먼저 istringstream으로 검증한다.
filesystem 디렉터리
<filesystem>(C++17)는 경로와 디렉터리를 다룬다: exists/create_directories/순회. 문자열로 경로를 이어 붙이는 것보다 견고하고, 구분자는 자동으로 플랫폼에 맞게 적용된다.
경로 분해
fs::path는 경로의 분해와 결합을 다루며, 플랫폼에 맞는 구분자를 자동으로 사용한다. filename/extension/parent_path로 경로 구성 요소를 얻는다.
바이너리 파일
바이너리 모드 std::ios::binary는 개행 문자를 변환하지 않으며, write/read는 바이트 블록 단위로 읽고 쓴다. 구조체를 디스크에 쓸 때는 메모리 정렬과 엔디언 호환에 주의한다.
표준 입력 검증
cin >>가 실패하면 스트림이 fail 상태가 되어 이후 모든 읽기가 실패한다. clear()로 상태를 지우고, ignore()로 더러운 데이터를 버린 뒤 계속한다. 읽은 후에는 항상 cin 상태를 검증한다.
13.흔한 실수
열 가지 C++ 고전적 안티패턴(BAD)과 정답(GOOD) - 빨강/초록 배색으로 항목별로 대비한다.
부호 있는/없는 정수 혼합 연산
부호 있는 값과 부호 없는 값을 혼합해 비교할 때, 부호 있는 값이 자동으로 부호 없는 값으로 변환되어 -1이 거대한 부호 없는 수가 된다. 비교 전에 명시적으로 변환하거나 부호 여부를 통일한다.
댕글링 참조 반환
로컬 변수의 참조나 포인터를 반환하면 댕글링 참조가 되어, 함수 반환 후 메모리가 무효화되어 사용하는 것은 미정의 동작이다. 값을 반환하거나 객체의 수명을 연장한다.
기본 클래스 소멸자가 virtual이 아닌 경우
기본 클래스 포인터로 파생 객체를 delete할 때, 기본 클래스 소멸자는 반드시 virtual이어야 한다. 그렇지 않으면 파생 부분의 소멸이 실행되지 않아 리소스가 누수된다.
정수 나눗셈의 절단
정수끼리의 나눗셈 결과는 여전히 정수(절단)이며, double에 대입하기 전에 이미 소수부가 사라진다. 최소한 한쪽을 부동소수점으로 변환한 뒤 나눈다.
루프 안에서 문자열 연결
s = s + x는 매번 기존 문자열 전체를 복사해 새 임시 객체를 생성하므로 O(n²); reserve + +=는 제자리에서 추가해 O(n)이다. 대용량 연결에서 성능이 한 단계 차이 난다.
using namespace std
헤더 파일에서 using namespace std를 쓰면 그 파일을 포함하는 모든 파일이 std 심볼 전체를 노출해 모호함을 일으킨다. 정밀한 using이나 직접 std:: 한정을 사용한다.
i++ vs ++i
i++은 이전 값을 반환하고(복사/임시 필요), ++i는 바로 증가한다. int에서는 차이가 없지만, 사용자 정의 타입/이터레이터에서는 ++i가 복사를 한 번 덜 한다.
const 정확성
읽기 전용 인자는 const&로 사용해, 의미를 스스로 문서화하고 복사를 피한다. const 정확성으로 컴파일러가 잘못된 수정을 막아주고, 호출 측에서도 함수가 인자를 바꾸지 않는다는 사실을 알 수 있다.
함수 대신 매크로
매크로는 타입 검사에 참여하지 않고, 스코프 규칙을 따르지 않으며, 부작용이 있는 인자를 여러 번 평가하기 쉽다. 가능하면 const/constexpr 함수나 템플릿을 우선 사용한다.
루프에서 값 복사
범위 기반 for에서 값으로 받으면 각 요소를 복사한다; 큰 객체는 const auto&로 읽기 전용으로, auto&로 수정한다. 데이터가 작아 복사본이 필요할 때만 값으로 받는다.
14.스레드와 동시성
std::thread, 상호 배타 락, 원자 연산, 조건 변수와 비동기 작업.
스레드 생성
std::thread는 호출 가능한 객체(함수/람다)를 실행할 새 스레드를 만든다. 스레드 객체가 소멸될 때는 반드시 join 또는 detach가 되어 있어야 하며, 그렇지 않으면 std::terminate가 호출된다.
join과 detach
join은 현재 스레드를 블록해 자식 스레드가 끝날 때까지 기다리고; detach는 자식 스레드를 독립적으로 실행하게 하며, 분리한 뒤에는 다시 join할 수 없고, 접근하는 객체는 살아 있어야 한다.
mutex 상호 배타 락
mutex는 공유 데이터를 보호하는 상호 배타 락으로, 동시에 하나의 스레드만 보유할 수 있다. 수동으로 lock/unlock을 호출하면 빠뜨리기 쉬우니, lock_guard/unique_lock을 우선 사용한다.
lock_guard 자동 잠금
lock_guard는 RAII로 락을 관리한다: 생성 시 잠그고, 소멸 시 해제하여, 모든 종료 경로(예외 포함)에서 잠금이 해제됨을 보장한다. 이것이 락의 표준 방식이다.
원자 연산
std::atomic은 기본 타입에 대해 원자 연산을 제공해, 다중 스레드 환경에서 락 없이 수정할 수 있게 한다. fetch_add/load/store는 모두 원자 연산이며, 상호 배타 락보다 오버헤드가 적다.
조건 변수
condition_variable은 스레드가 조건을 기다리게 한다: wait는 락을 해제하고 블록하며, notify는 깨운다. wait는 가짜 깨우기를 막기 위해 반드시 술어를 인자로 가져야 한다.
async / future
std::async는 비동기 작업을 시작해 future를 반환하고, get()은 결과를 얻기 위해 블록한다. 직접 thread+공유 변수를 쓰는 것보다 간단하다; launch::async를 지정해 새 스레드를 강제할 수 있다.
thread_local
thread_local 변수는 각 스레드마다 독립 복본을 가져, 락 없이 캐시, 카운터, 임시 상태로 자연스럽게 쓸 수 있다. 스레드 종료 시 소멸되며, 스레드 전용 데이터에 적합하다.
데이터 경쟁
두 스레드가 같은 비원자 변수를 동시에 읽고 쓰는 것은 데이터 경쟁(미정의 동작, 결과를 예측할 수 없음)이다. atomic이나 mutex로 동기화한다.
병렬 알고리즘
C++17의 <execution>은 알고리즘에 실행 정책을 추가한다: par는 병렬, unseq는 벡터화. 대용량에서 자동으로 멀티코어를 활용하며, 전제는 원소가 공유 가능한 가변 상태를 갖지 않는 것이다.
15.네트워크 (소켓)
POSIX 소켓의 생성, 리슨, 연결, 송수신과 타임아웃 제어 (크로스 플랫폼 네트워크는 Boost.Asio나 libcurl 사용을 권장).
소켓 생성
POSIX 소켓은 네트워크 I/O의 기반이다: socket()로 소켓을 만들고, AF_INET은 IPv4, SOCK_STREAM은 TCP다. 파일 디스크립터를 반환하며, -1은 실패를 의미한다.
바인딩과 리슨
bind는 소켓을 포트에 바인딩하고, listen은 수신 대기를 시작한다. htons는 호스트 바이트 순서를 네트워크 바이트 순서로 변환한다. sockaddr_in은 IPv4 주소 구조체다.
accept로 연결 수락
accept는 대기열에서 클라이언트 연결을 꺼내 새 소켓을 반환하며, 각 연결마다 fd 하나가 부여된다. 원래의 수신 대기 fd는 계속해서 다음 연결을 accept한다.
클라이언트 연결
클라이언트 측에서 socket() + connect()로 서버 주소에 접속한다. inet_pton은 점으로 구분된 십진수 IP를 이진 주소로 변환한다. connect 실패 시 -1을 반환한다.
송신과 수신
send/recv는 TCP 소켓에서 바이트를 송수신한다. recv가 0을 반환하면 상대방이 연결을 닫았다는 뜻이고, -1은 오류다. TCP는 스트림 프로토콜이므로 메시지 경계를 직접 정의해야 한다.
도메인 이름 해석
getaddrinfo는 호스트 이름과 서비스를 접속 가능한 주소 리스트로 변환하며, IPv4/IPv6를 자동으로 처리한다. inet_pton + 포트를 손으로 작성하는 방식 대신 권장되는 방법이다.
타임아웃 제어
SO_RCVTIMEO는 recv에 타임아웃을 설정한다. 타임아웃 시 -1을 반환하며 errno는 EWOULDBLOCK이 된다. 블록킹 recv가 이로 인해 제어 가능해져 영구 정지를 피할 수 있다.
최소 HTTP 요청
HTTP 요청은 텍스트 프로토콜이다: 요청 라인 + 헤더 + 빈 줄. 여기서는 소켓 메시지를 직접 보내는 방식을 보여준다. 실제 운영 환경에서는 리디렉션, TLS, 압축을 자동 처리해 주는 libcurl을 우선 사용하라.
16.시간과 날짜
chrono의 duration / time_point, 두 종류의 시계, 포맷, 슬립과 소요 시간 측정.
duration (기간)
duration은 시간 길이를 나타내며 타입이 단위를 갖는다: seconds / milliseconds / microseconds. 리터럴 접미사 s/ms/us가 직관적이며, duration_cast로 단위를 변환한다.
time_point (시각)
time_point은 시간축 위의 한 시점이며, 시계 + duration 오프셋으로 구성된다. 시각에 duration을 더하거나 빼면 새로운 시각이 되고, 두 시각을 빼면 duration이 된다.
system_clock
system_clock은 시스템 벽시계에 대응하며, time_t와 상호 변환(to_time_t/from_time_t)할 수 있다. 달력 시간 표시나 로그 타임스탬프에 사용된다. 시스템 시간 변경의 영향을 받는다.
steady_clock
steady_clock은 단조 증가하며 시스템 시간 변경의 영향을 받지 않아, 시간 측정(소요 시간 측정, 타임아웃 판정)의 기준이 된다. 달력 시간을 표시할 때만 system_clock을 쓴다.
시간 포맷
put_time은 strftime 포맷에 따라 time_t를 로컬 시간 문자열로 포맷한다. %Y-%m-%d %H:%M:%S가 가장 자주 쓰인다. localtime은 정적 버퍼를 반환하므로 스레드 안전하지 않다.
스레드 슬립
sleep_for는 지정된 기간 동안 현재 스레드를 멈추게 하고, sleep_until은 지정된 시각까지 멈춘다. 메인 스레드를 멈추면 UI가 정지하므로 백그라운드 / 테스트 용도로만 사용한다.
소요 시간 측정
시간 측정 관례: start에서 시작점을 기록하고, 종료 시 시작점을 빼서 duration을 구한 뒤 duration_cast로 단위를 변환하고 count()로 값을 꺼낸다. steady_clock은 시간 변경의 영향을 받지 않음을 보장한다.
C 스타일 시간 상호 변환
time_t는 초 단위 타임스탬프이며, gmtime은 UTC 구조체로, localtime은 로컬 구조체로 변환하고 strftime이 포맷한다. 이 함수들은 정적 버퍼를 사용해 스레드 안전하지 않다는 점에 주의하라.
시간대
C++20의 zoned_time은 시간대를 적용해 시간을 출력하며, 서머타임을 자동으로 처리한다. 구 표준 라이브러리에는 시간대가 없어 시스템 localtime이나 서드파티 라이브러리에 의존해야 한다.
17.프로세스와 시그널
system, fork/exec/wait 자식 프로세스, 환경 변수, 시그널 처리와 파이프 출력 캡처 (POSIX 개념).
system으로 명령 실행
system()은 셸을 호출해 문자열 명령을 실행한다. 간단하지만 안전하지 않다(커맨드 인젝션, 셸 차이). 출력을 캡처하거나 인자를 다뤄야 할 때는 fork + exec 또는 popen을 쓴다.
fork로 프로세스 복제
fork는 현재 프로세스를 자식 프로세스로 복제하며 두 번 반환된다: 부모 프로세스는 자식의 pid를, 자식 프로세스는 0을 받는다. -1은 실패를 뜻한다. fork 이후 자식은 fork 지점부터 실행을 이어간다.
exec으로 프로그램 교체
exec 계열은 현재 프로세스 내에서 새 프로그램을 적재해 자신을 교체한다. fork + exec 조합으로 외부 명령을 기동한다. exec은 성공 시 반환되지 않고 실패 시 -1을 반환하므로, 자식 프로세스는 실패 경로를 처리해야 한다.
wait으로 자식 프로세스 대기
waitpid는 지정한 자식 프로세스의 종료를 기다리며 종료 상태를 가져온다. WIFEXITED는 정상 종료 여부를 판정하고, WEXITSTATUS는 종료 코드를 얻는다. wait하지 않은 자식은 좀비 프로세스가 된다.
환경 변수
getenv는 환경 변수를 읽고(없으면 nullptr 반환), setenv는 설정, unsetenv는 삭제한다. 환경 변수는 부모 프로세스가 자식 프로세스로 전달하는 간단한 설정 통로다.
시그널 처리
signal은 시그널 핸들러를 등록하며, SIGINT(Ctrl+C), SIGTERM(kill 기본값) 등을 처리한다. 시그널 핸들러 안에서는 비동기 안전한 연산만 수행해야 하며, 힙 할당이나 I/O를 사용해서는 안 된다.
sigaction으로 신뢰성 있는 시그널
sigaction은 signal보다 더 신뢰할 수 있다: 시그널 마스크와 플래그를 설정할 수 있다. SA_RESTART는 시그널에 의해 중단된 블록킹 호출을 자동으로 재개한다. 운영 코드에서는 sigaction을 우선 사용하라.
exit와 _exit
exit은 프로세스를 종료하며 atexit 정리 함수를 실행하고 버퍼를 플러시한다. _exit은 정리 없이 즉시 종료한다. fork 이후의 자식은 부모 자원의 중복 정리를 피하기 위해 _exit을 써야 한다.
popen으로 파이프 출력 가져오기
popen은 명령을 실행하고 파이프로 출력을 읽는다("r" 읽기 / "w" 입력 쓰기). system보다 출력을 캡처할 수 있다. pclose는 닫고 기다리며 반환 값에 종료 상태가 포함된다.
18.정규 표현식
std::regex의 매칭, 검색, 치환, 캡처 그룹, 플래그와 로 문자열 작성법.
기본 매칭
std::regex는 기본적으로 ECMAScript 문법을 사용한다. regex_match는 문자열 전체 매칭을 요구하고, regex_search는 부분 문자열을 찾는다. R"()" 로 문자열을 쓰면 이스케이프를 피할 수 있다.
전체 매칭과 그룹
regex_search는 첫 번째로 매칭된 부분 문자열을 찾고, smatch는 전체 매칭(m[0])과 캡처 그룹(m[1]…)을 담는다. regex_match는 전체 문자열 매칭을 요구하므로 형식 검증에 적합하다.
모든 매칭 순회
regex_search를 루프로 돌리면 직전 매칭의 끝에서부터 이어서 모든 매칭을 순회한다. m.suffix().first는 매칭 이후 남은 부분의 시작점이다. 또는 regex_iterator를 직접 써도 된다.
치환
regex_replace는 모든 매칭을 전역 치환한다. 치환 템플릿에서 $&는 매칭 전체, $1은 캡처 그룹을 참조한다. 위치별로 치환해야 할 때는 regex_iterator를 쓴다.
이터레이터로 일괄 추출
sregex_iterator는 "모든 매칭 찾기"를 이터레이터로 캡슐화한다. 순회만 하면 모든 결과를 얻을 수 있어 while regex_search를 직접 작성하는 것보다 간결하며, 일괄 추출에 적합하다.
캡처 그룹
캡처 그룹은 괄호로 묶으며, smatch는 인덱스로 접근한다. (?:...)는 캡처하지 않는 그룹으로, 논리적인 묶음만 하고 번호를 차지하지 않는다. 이름 있는 그룹 (?<name>...)은 m["name"]으로 접근한다.
매칭 플래그
std::regex 생성자의 두 번째 인자는 플래그다: icase는 대소문자를 무시하고, multiline은 ^/$가 줄의 시작/끝에 매칭되게 하며, ECMAScript/extended는 문법 변종을 선택한다. 여러 개는 | 로 조합한다.
로 문자열로 정규식 작성
R"(...)" 안에서는 역슬래시가 그대로 유지되어 정규식과 경로를 이스케이프 없이 쓸 수 있다. )" 가 포함된 문자열은 R"tag(...)tag" 처럼 사용자 정의 구분자를 쓴다. 정규식을 작성할 때는 항상 로 문자열을 우선 사용하라.
정규식 예외
잘못된 정규식은 생성 시 std::regex_error를 던지므로, 이를 잡아 프로그램이 깨지지 않도록 한다. 컴파일 비용이 큰 정규식은 객체를 재사용해 매번 다시 컴파일하지 않도록 한다.
19.빌드와 디버깅
컴파일 플래그, Makefile / CMake, 포맷팅, sanitizer, 디버거와 성능 프로파일링.
자주 쓰는 컴파일 플래그
자주 쓰는 플래그: -std는 표준을, -O는 최적화 단계를, -g는 디버그 정보를, -Wall -Wextra는 경고를, -Werror는 경고를 오류로 다룬다. 다중 파일은 먼저 -c로 개별 컴파일한 뒤 링크한다.
Makefile
Makefile은 타깃과 의존 관계를 선언하고, make가 의존 관계에 따라 자동으로 다시 빌드한다. $<는 첫 번째 의존, $@는 타깃, $^는 모든 의존이다. 규칙 줄은 반드시 Tab으로 들여 써야 한다.
CMake
CMake는 선언적 빌드 시스템으로, 크로스 플랫폼으로 Makefile/VS 프로젝트를 생성한다. target_link_libraries는 라이브러리를 링크하고, find_package는 의존성을 찾는다. 빌드는 별도의 build/ 디렉터리에서 수행한다.
pkg-config로 라이브러리 파라미터 조회
pkg-config는 라이브러리의 컴파일/링크 파라미터를 조회한다. $(pkg-config ...)로 명령 치환해 명령줄에 주입함으로써 경로를 손으로 쓰지 않아도 된다. 서드파티 라이브러리를 링크하는 표준 방식이다.
clang-format 포맷팅
clang-format은 코드를 자동으로 포맷하며, .clang-format 파일로 스타일을 통일한다. ColumnLimit는 줄 너비를 제어하고, SortIncludes는 헤더 파일을 정렬한다. 커밋 전에 한 번 돌려 일관성을 유지한다.
Sanitizer 검사
컴파일 시 sanitizer를 켜면 실행 시점에 메모리/동시성 오류를 잡아낸다: address는 경계 초과와 누수를, undefined는 미정 동작을, thread는 데이터 경쟁을 잡는다. 테스트 시에는 반드시 켜라.
gdb 디버거
gdb는 명령줄 디버거이며, -g로 컴파일해 심볼을 주입한다. break는 브레이크포인트, run은 실행 시작, bt는 스택 확인, print는 값 확인이다. UI 디버거(VSCode/CLion)의 기반도 결국 이것이다.
valgrind 메모리 검사
valgrind는 실행 시점의 메모리 문제를 검사한다: 초기화되지 않은 읽기, 경계 초과, 누수, 이중 해제. --leak-check=full은 각 누수의 호출 스택을 출력한다. 속도가 느리므로 테스트의 일부만 돌리면 된다.
정적 라이브러리 패키징
ar은 여러 .o를 묶어 lib*.a 형태의 정적 라이브러리로 만든다. -L은 라이브러리 디렉터리를, -l은 라이브러리 이름을 지정한다(lib 접두사와 .a 접미사는 생략). 정적 라이브러리는 실행 파일에 포함되어 런타임 의존성이 없다.
성능 프로파일링
gprof/perf는 핫스팟 함수를 분석한다. g++ -pg로 샘플링을 주입하고 실행 후 gprof가 각 함수의 호출 횟수와 소요 시간 비율을 출력해 최적화 대상을 찾아낸다.
이 치트시트에 대해
본 페이지는 C++17/C++20의 자족적인 치트시트로, 언어 핵심, 표준 템플릿 라이브러리(STL), 빌드 도구 체인이 실제 프로젝트에서 약 80%의 흔한 용법을 커버한다. 내용은 현대적 관용구에 치중한다: 스마트 포인터(unique_ptr / shared_ptr), 이동 의미론, auto 타입 추론, 범위 기반 for, 구조화된 바인딩, 그리고 C++20의 concepts와 ranges. C++은 Bjarne Stroustrup이 1985년에 만들었으며, C의 고성능과 고수준 추상화를 겸비해 시스템 프로그래밍, 게임 엔진, 고성능 컴퓨팅 분야의 토대 언어다. 19개 섹션이 각자 한 주제에 집중한다: 기초 문법, 변수, 타입과 포인터, 제어 흐름, 함수, 문자열, 컬렉션, 메모리 관리, 객체 지향, 오류 처리, 입출력, 흔한 오해, 동시성, 네트워크, 시간, 프로세스, 정규식과 빌드 도구. 각 소절에는 「개념 소개 + 바로 복사해 쓸 수 있는 코드 스니펫」이 함께 제공되어, 찾아보고 실험하기 편하다. 모든 코드와 텍스트는 브라우저에서 로컬로 렌더링되며, 어떤 데이터도 기기를 떠나지 않는다. 권위 있는 참고는 cppreference와 ISO C++ 표준 초안을 보라.
버전 2.1.0