
C++를 쓰다 보면 표준 라이브러리(STL)를 얼마나 잘 아느냐에 따라 코드 품질이 꽤 달라집니다. 직접 자료구조를 구현하지 않아도 되고, 이미 최적화가 잘 된 구현을 그냥 쓰면 됩니다. 그런데 너무 그 header 종류가 방대해서 맨날 까먹고... 정리를 해야겠다고 마음을 먹었습니다.
STL은 크게 컨테이너, 알고리즘, 이터레이터, 유틸리티로 나뉩니다. 1편에서는 컨테이너를 정리합니다.
헤더 포함
각 컨테이너는 해당 헤더를 포함해야 씁니다.
#include <vector>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
#include <string>
vector
동적 배열입니다. 가장 자주 쓰는 컨테이너입니다. 연속된 메모리에 저장돼서 캐시 효율이 좋습니다.
#include <vector>
std::vector<int> v;
std::vector<int> v2 = {1, 2, 3, 4, 5};
std::vector<int> v3(10, 0); // 크기 10, 초기값 0
std::vector<int> v4(v2); // 복사 생성
// 원소 추가
v.push_back(10);
v.emplace_back(20); // push_back보다 효율적 (직접 생성)
// 접근
v[0]; // 범위 검사 없음
v.at(0); // 범위 검사 있음 (예외 발생)
v.front(); // 첫 번째
v.back(); // 마지막
// 크기
v.size(); // 원소 수
v.capacity(); // 할당된 용량
v.empty(); // 비어있는지
v.reserve(100); // 용량 미리 확보 (재할당 방지)
v.resize(20); // 크기 변경
v.shrink_to_fit(); // 여분 용량 반납
// 삭제
v.pop_back(); // 마지막 제거
v.erase(v.begin()); // 특정 위치 제거
v.erase(v.begin(), v.begin()+3); // 범위 제거
v.clear(); // 전체 제거
// 삽입
v.insert(v.begin() + 2, 99); // 특정 위치에 삽입
// 이터레이터
for (auto& x : v) { ... } // 범위 기반 for
for (auto it = v.begin(); it != v.end(); ++it) { ... }
push_back vs emplace_back — emplace_back은 인수를 그대로 전달해서 컨테이너 안에서 직접 생성합니다. 임시 객체를 만들고 복사하는 push_back보다 효율적인 경우가 많습니다.
array
크기가 컴파일 시점에 고정된 배열입니다. C 스타일 배열 대신 씁니다. 스택에 할당되고 오버헤드가 없습니다.
#include <array>
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::array<int, 5> arr2; // 초기화 안 됨
arr[0]; // 접근
arr.at(0); // 범위 검사
arr.size(); // 항상 5
arr.front();
arr.back();
arr.fill(0); // 전체를 0으로
deque
양방향 큐입니다. vector와 비슷하지만 앞뒤 모두 O(1)으로 삽입/삭제가 됩니다.
#include <deque>
std::deque<int> dq = {3, 4, 5};
dq.push_front(2); // 앞에 추가
dq.push_back(6); // 뒤에 추가
dq.pop_front(); // 앞에서 제거
dq.pop_back(); // 뒤에서 제거
dq[0]; // 임의 접근 (vector처럼)
list
이중 연결 리스트입니다. 중간 삽입/삭제가 O(1)이지만 임의 접근은 없습니다. 캐시 효율이 나쁘고 메모리 오버헤드가 있어서 실제로 쓸 일이 생각보다 적습니다.
#include <list>
std::list<int> lst = {1, 2, 3};
lst.push_back(4);
lst.push_front(0);
lst.pop_back();
lst.pop_front();
// 정렬, 역순, 중복 제거
lst.sort();
lst.reverse();
lst.unique(); // 연속된 중복 제거 (정렬 후 쓰면 전체 중복 제거)
// 이터레이터로만 접근 (임의 접근 없음)
auto it = lst.begin();
std::advance(it, 2); // it를 2칸 앞으로
lst.insert(it, 99);
lst.erase(it);
map / multimap
키-값 쌍을 저장합니다. 키 기준 정렬된 상태를 유지합니다(레드블랙 트리 기반). 탐색/삽입/삭제 모두 O(log n)입니다.
#include <map>
std::map<std::string, int> m;
// 삽입
m["apple"] = 3;
m.insert({"banana", 5});
m.emplace("cherry", 7);
// 접근
m["apple"]; // 없으면 기본값으로 생성됨 (주의)
m.at("apple"); // 없으면 예외
// 탐색
auto it = m.find("apple");
if (it != m.end()) {
std::cout << it->second;
}
m.count("apple"); // 있으면 1, 없으면 0
// 순회 (키 기준 정렬 순)
for (auto& [key, val] : m) {
std::cout << key << ": " << val;
}
// 삭제
m.erase("apple");
m.erase(m.begin());
// 기타
m.size();
m.empty();
m.contains("apple"); // C++20
m["key"]는 키가 없으면 기본값으로 새로 만듭니다. 읽기만 하려면 find()나 at()을 씁니다.
multimap은 같은 키를 여러 개 허용합니다. [] 연산자는 없습니다.
unordered_map / unordered_multimap
해시 기반 맵입니다. 정렬은 없지만 평균 O(1) 탐색/삽입/삭제입니다. 순서가 필요 없으면 map보다 빠릅니다.
#include <unordered_map>
std::unordered_map<std::string, int> um;
um["apple"] = 3;
um.insert({"banana", 5});
auto it = um.find("apple");
um.count("apple");
um.contains("apple"); // C++20
// 버킷 관련
um.bucket_count();
um.load_factor();
um.reserve(100); // 예상 원소 수 미리 알려줌 (재해시 방지)
사용자 정의 타입을 키로 쓰려면 해시 함수를 직접 만들어야 합니다.
struct Point { int x, y; };
struct PointHash {
size_t operator()(const Point& p) const {
return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 1);
}
};
struct PointEqual {
bool operator()(const Point& a, const Point& b) const {
return a.x == b.x && a.y == b.y;
}
};
std::unordered_map<Point, int, PointHash, PointEqual> pm;
set / multiset
정렬된 집합입니다. 중복을 허용하지 않습니다(multiset은 허용). 내부는 map과 같이 레드블랙 트리입니다.
#include <set>
std::set<int> s = {3, 1, 4, 1, 5}; // {1, 3, 4, 5} — 중복 제거, 정렬
s.insert(2);
s.erase(3);
s.find(4); // 이터레이터 반환
s.count(1); // 있으면 1
s.contains(1); // C++20
// 범위 탐색
auto lower = s.lower_bound(3); // 3 이상 첫 번째
auto upper = s.upper_bound(3); // 3 초과 첫 번째
unordered_set
해시 기반 집합입니다. 순서 없고 평균 O(1).
#include <unordered_set>
std::unordered_set<int> us = {1, 2, 3, 4};
us.insert(5);
us.erase(3);
us.contains(2); // C++20
stack / queue / priority_queue
어댑터 컨테이너입니다. 내부적으로 다른 컨테이너를 감쌉니다.
stack — LIFO
#include <stack>
std::stack<int> st;
st.push(1);
st.push(2);
st.top(); // 2 (제거 없이 보기)
st.pop(); // 제거만 (반환 없음)
st.empty();
st.size();
queue — FIFO
#include <queue>
std::queue<int> q;
q.push(1);
q.push(2);
q.front(); // 맨 앞
q.back(); // 맨 뒤
q.pop();
priority_queue — 최대 힙 (기본)
std::priority_queue<int> pq; // 최대 힙
pq.push(3);
pq.push(1);
pq.push(4);
pq.top(); // 4 (가장 큰 값)
pq.pop();
// 최소 힙
std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
string
#include <string>
std::string s = "Hello";
std::string s2(5, 'a'); // "aaaaa"
// 접근
s[0];
s.at(0);
s.front();
s.back();
// 연결
s + " World";
s += "!";
s.append(" more");
// 탐색
s.find("ll"); // 2 (위치), npos면 없음
s.rfind("l"); // 뒤에서 찾기
s.find_first_of("aeiou"); // 모음 중 처음 나오는 위치
// 부분 문자열
s.substr(1, 3); // 1번 위치부터 3글자
// 변환
std::to_string(42);
std::stoi("42");
std::stod("3.14");
// 비교, 크기
s.size();
s.empty();
s.compare("Hello");
// 치환
s.replace(0, 5, "Hi");
// 제거
s.erase(1, 3); // 1번 위치부터 3글자 제거
컨테이너 선택 기준
| 상황 | 선택 |
|---|---|
| 기본 배열, 빠른 임의 접근 | vector |
| 크기 고정 배열 | array |
| 앞뒤 삽입/삭제 모두 필요 | deque |
| 중간 삽입/삭제 빈번 | list |
| 키-값, 정렬 필요 | map |
| 키-값, 속도 우선 | unordered_map |
| 중복 없는 집합, 정렬 필요 | set |
| 중복 없는 집합, 속도 우선 | unordered_set |
| LIFO | stack |
| FIFO | queue |
| 우선순위 큐 | priority_queue |
2편에서는 알고리즘, 이터레이터, 람다와 함수형 유틸리티, C++17/20 추가 유틸리티를 정리합니다.
'블로그, 컴퓨터 > Cheatsheets' 카테고리의 다른 글
| C++ STL 정리 (3편) — C++17/20 유틸리티 (0) | 2026.06.01 |
|---|---|
| C++ STL 정리 (2편) — 알고리즘, 이터레이터, 유틸리티 (0) | 2026.06.01 |
| GitHub Actions 정리 (2편) — 캐싱, Matrix, 재사용, 실전 패턴 (0) | 2026.05.31 |
| GitHub Actions 정리 (1편) — 개념, 워크플로우 구조, 트리거 (0) | 2026.05.31 |
| CMake 정리 (2편) — 서브디렉토리, 외부 라이브러리, 실용 패턴 (0) | 2026.05.30 |