CS โ pushback vs emplaceback
๐ 04/30 โ vector push_back vs emplace_back + ํด์ ์ถฉ๋ ๋ณด๊ฐ
๋ชจ์๋ฉด์ ๋ค์ ์ฃผ์ : โ
vector์push_back๊ณผemplace_back์ ์ฐจ์ด์ ์ ๋ํด ์ค๋ช ํด ์ฃผ์ธ์โ 14๋ฒ ๋ชจ์๋ฉด์ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ ๋ณด๊ฐ: ํด์ ์ถฉ๋(Hash Collision) ์ฒ๋ฆฌ โ ์ฒด์ด๋ vs ์คํ ์ด๋๋ ์ฑ + vector capacity / rehash ์ ๋ฆฌ
ํ์ต ์์ญ ์ ํ์ โ ์ปจํ ์ด๋ ์ฌ์ฉ๋ฒ + 14๋ฒ ๋ณด๊ฐ ํ ๋ฌถ์
1
2
3
4
5
6
13๋ฒ vector vs list โ ์ํ์ค ์ปจํ
์ด๋ ๋ฉ๋ชจ๋ฆฌยท์บ์
14๋ฒ std::map โ ์ฐ๊ด ์ปจํ
์ด๋ (RB-Tree)
14๋ฒ ํ์ std::map followup โ ๋ชจ์๋ฉด์ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ 16๊ฐ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
15๋ฒ push_back vs emplace_back โ
โ vector ๊ด์ฉ๊ตฌ + ํด์ ์ถฉ๋ + capacity
์ดํ std::unordered_map deepdive โ ํด์ ์๋ฃ๊ตฌ์กฐ ๋จ๋
์ ๋ฆฌ
๋ณธ ๋ฌธ์๋ ๋ ๊ฐ์ง ๊ฐ๋๋ฅผ ํ ํ์ผ์ ๋ฌถ๋๋ค:
- ๋ค์ ์ฃผ์ ๋ต๋ณ โ push_back vs emplace_back (1ยท2๋ถ)
- 14๋ฒ ๋ณด๊ฐ โ ํด์ ์ถฉ๋ ์ฒ๋ฆฌ(์ฒด์ด๋/์คํ ์ด๋๋ ์ฑ) + vector capacity/rehash (3ยท4๋ถ)
๋ชจ์๋ฉด์ ๋ต๋ณ
vector::push_back ๊ณผ emplace_back ์ ๋ ๋ค ๋์ ์์๋ฅผ ์ถ๊ฐํ๋ ๋ฉค๋ฒ ํจ์์ด์ง๋ง, ๊ฐ์ฒด ์์ฑ ์์น์ ์ธ์ ์ ๋ฌ ๋ฐฉ์์ด ๋ค๋ฆ
๋๋ค.
push_back(value) ์ ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๋ฐ์ vector ์ ๋ง์ง๋ง ์ฌ๋กฏ์ ๋ณต์ฌ ๋๋ ์ด๋ํด ๋ฃ์ต๋๋ค. ๋ฐ๋ผ์ push_back(T(args...)) ์ฒ๋ผ ์์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ๊ทธ๊ฒ์ ๋ค์ ์ฎ๊ธฐ๋ 2๋จ๊ณ ๋น์ฉ์ด ๋ค ์ ์์ต๋๋ค. C++11 ๋ถํฐ rvalue ์ค๋ฒ๋ก๋๊ฐ ์ถ๊ฐ๋ผ ์์ ๊ฐ์ฒด๋ ์ด๋๋์ง๋ง, ์ฌ์ ํ ์์ ๊ฐ์ฒด ์์ฑ ์์ฒด์ ๋น์ฉ์ ๋จ์ต๋๋ค.
emplace_back(args...) ์ vector ์ ๋ง์ง๋ง ์ฌ๋กฏ ๋ฉ๋ชจ๋ฆฌ ์์์ ์ง์ ์์ฑ์๋ฅผ ํธ์ถํฉ๋๋ค. ๊ฐ๋ณ ํ
ํ๋ฆฟ + perfect forwarding ์ผ๋ก ์ธ์๋ฅผ ๊ทธ๋๋ก ์์ฑ์์ ์ ๋ฌํ๋ฏ๋ก ์์ ๊ฐ์ฒด ์์ฒด๊ฐ ๋ง๋ค์ด์ง์ง ์์ต๋๋ค. ์์ฑ์ ์ธ์๊ฐ ์ฌ๋ฌ ๊ฐ์ธ ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด๋ ๋ณต์ฌ๊ฐ ๊ธ์ง๋ ํ์
(unique_ptr ๋ฑ)์ฒ๋ผ in-place ์์ฑ์ด ๋ณธ์ง์ ์ผ๋ก ํ์ํ ์ํฉ์์ ์๋ฏธ์๋ ์ฐจ์ด๊ฐ ๋ฉ๋๋ค.
1
2
3
4
5
6
7
std::vector<std::string> v;
v.push_back("Alice"); // const char* โ string ์์ ์์ฑ โ ์ด๋
v.emplace_back("Alice"); // ์ฌ๋กฏ์์ string("Alice") ์ง์ ์์ฑ
std::vector<std::pair<int, std::string>> p;
p.push_back({1, "A"}); // pair ์์ ์์ฑ โ ์ด๋
p.emplace_back(1, "A"); // pair(1, "A") in-place ์์ฑ
๋ค๋ง emplace_back ์ด ํญ์ ๋ ๋น ๋ฅด๊ฑฐ๋ ๋ ์์ ํ ๊ฑด ์๋๋๋ค. explicit ์์ฑ์๋ฅผ ์๋์น ์๊ฒ ์ฐํํ ์ ์๊ณ , ํ์
์ ๋ช
์์ ์ผ๋ก ๋ณด์ฌ์ฃผ์ง ์์ ์ฝ๋ ์๋๋ฅผ ํ๋ฆด ์ ์์ต๋๋ค. ์๋๊ฐ โ์ด ๊ฐ์ ์ถ๊ฐํ๋ผโ ๋ฉด push_back, โ์ฌ๊ธฐ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฃ์ด๋ผโ ๋ฉด emplace_back ์ด ์๋ฏธ์ ๋ ์ ํํฉ๋๋ค.
์ฑ๋ฅ ์ฐจ์ด๊ฐ ๊ฒฐ์ ์ ์ธ ๊ฒฝ์ฐ๋ ์์ ๋งํ ์์ฑ์ ์ธ์๊ฐ ์ฌ๋ฌ ๊ฐ์ธ ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด ๋๋ ๋ณต์ฌ๊ฐ ๊ธ์ง๋ ํ์ ์ in-place ์์ฑ์ ๋๋ค. POD ํ์ ์ด๋ ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ์ฎ๊ธธ ๋๋ ๋ ํจ์์ ๋น์ฉ์ด ์ฌ์ค์ ๊ฐ์ต๋๋ค(๋๋ถ๋ถ ์ปดํ์ผ๋ฌ ์ต์ ํ๋ก ๋์ผ ์ฝ๋ ์์ฑ).
์ฌํ ์ง๋ฌธ โ reserve ยท capacity ยท ๊ฐ์ฒด ์์ฑ ์์ธ
push_back ์ ์ธ์๋ฅผ 1๊ฐ๋ง ๋ฐ๋๋ค๋ ๊ฒ ๋ฌด์จ ๋ป์ธ๊ฐ?
push_back ์๊ทธ๋์ฒ๋ push_back(const T&) ๋๋ push_back(T&&) ๋ก ์ธ์ 1๊ฐ๋ง ๋ฐ์ต๋๋ค. ์ฌ๋ฌ ์์ฑ์ ์ธ์๋ฅผ ๋๊ธฐ๋ ค๋ฉด ๊ฐ์ฒด๋ฅผ ๋จผ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค. push_back(1, 2) ๋ ์ปดํ์ผ ์๋ฌ์ด๊ณ , push_back(Point(1, 2)) ์ฒ๋ผ ๊ฐ์ฒด 1๊ฐ๋ก ๊ฐ์ธ์ ์ ๋ฌํด์ผ ํฉ๋๋ค. emplace_back(1, 2) ๋ ๊ฐ๋ณ ํ
ํ๋ฆฟ์ผ๋ก ์ธ์ ์ฌ๋ฌ ๊ฐ๋ฅผ ๋ฐ์ ์ฌ๋กฏ์์ ์ง์ Point(1, 2) ๋ฅผ ์์ฑํ๋ฏ๋ก ์ด ์ ์ฝ์ด ์์ต๋๋ค.
reserve ๋ capacity ๋ฅผ ๋๋ฆฌ๋๊ฐ, size ๋ฅผ ๋๋ฆฌ๋๊ฐ?
reserve(n) ์ capacity ๋ง ๋๋ฆฌ๊ณ size ๋ ๊ทธ๋๋ก ์
๋๋ค. ํ์ n * sizeof(T) ๋ฐ์ดํธ์ง๋ฆฌ ์ฐ์ ๋ธ๋ก์ ๋ฏธ๋ฆฌ ํ ๋นํ๋ ๊ฒ์ด๊ณ , ์์๋ ์ดํ push_back / emplace_back ์ ํด์ผ ์๊น๋๋ค. size ๋ฅผ ์ง์ ๋๋ฆฌ๋ ค๋ฉด resize(n) ์ ์จ์ผ ํ๋ฉฐ, ์ด ๊ฒฝ์ฐ ์์ n ๊ฐ๊ฐ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐํ๋ฉ๋๋ค.
reserve ์ ๋ฉ๋ชจ๋ฆฌยท์บ์ ๋์
reserve ๋ ํ์ ์ฐ์ ๋ธ๋ก์ ํ ๋ฒ์ ํ ๋นํฉ๋๋ค. size ๋ฒ์ ์๋ง ์ด๊ธฐํ๋ ์์์ด๊ณ , ๋๋จธ์ง๋ raw ๋ฉ๋ชจ๋ฆฌ์
๋๋ค. ์บ์ ๊ด์ ์์ ์ฌํ ๋น์ด ์ผ์ด๋๋ฉด ์ ์ฃผ์๋ก ๋ธ๋ก์ด ํต์งธ๋ก ์ด๋ํ๋ฉด์ ์บ์ ๋ผ์ธ์ด ๋ฌดํจํ๋ฉ๋๋ค. reserve ๋ ์ด ์ฌํ ๋น ์์ฒด๋ฅผ ๋ง์ ๊ฐ์ ์ฃผ์๋ฅผ ๊ณ์ ์ฌ์ฉํ๊ฒ ํ๊ณ , ์ฐ์ ๋ฐฐ์ด์ด๋ฏ๋ก CPU prefetch ๋ ์ ํํ ์์ธก๋ฉ๋๋ค.
reserve ๋ฅผ ์ด๊ณผํ๋ฉด UB ์ธ๊ฐ?
์๋๋๋ค. capacity ๋ฅผ ์ด๊ณผํด์ push_back ํ๋ฉด ์๋ ์ฌํ ๋น์ด ์ผ์ด๋๋ฏ๋ก UB ๊ฐ ์๋๋๋ค. UB ๋ size ๋ฅผ ์ด๊ณผํ ์ธ๋ฑ์ค๋ก ์ง์ ์ ๊ทผํ ๋ ๋ฐ์ํฉ๋๋ค. reserve(10) ํ size ๊ฐ 0์ธ ์ํ์์ v[0] ์ ์ ๊ทผํ๋ฉด ์ด๊ธฐํ๋์ง ์์ raw ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฑด๋๋ฆฌ๋ ๊ฒ์ด๋ฏ๋ก UB ์
๋๋ค. ์ ๊ทผ ๊ฐ๋ฅ ๋ฒ์์ ๊ธฐ์ค์ capacity ๊ฐ ์๋๋ผ size ์
๋๋ค.
reserve ๋ฅผ ์ฐ๋ฉด push_back ๊ณผ emplace_back ์ ์ฐจ์ด๊ฐ ์์๋๋๊ฐ?
์๋๋๋ค. ๋์ ๋ค๋ฅธ ์ถ์ ๊ฐ๋
์
๋๋ค. reserve ๋ ์ฌํ ๋น ํ์๋ฅผ ์ค์ด๋ ๊ฒ์ด๊ณ , push_back vs emplace_back ์ ์ฐจ์ด๋ ์์ ๊ฐ์ฒด ์์ฑ ์ฌ๋ถ์
๋๋ค. reserve ๋ก ์ฌํ ๋น์ ์์ ๋, push_back(Point(1,2)) ๋ ์์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ๋กฏ์ผ๋ก ์ด๋ํ๋ 2๋จ๊ณ์ด๊ณ , emplace_back(1, 2) ๋ ์ฌ๋กฏ์์ ์ง์ ์์ฑํ๋ 1๋จ๊ณ์ธ ๊ฒ์ ๊ทธ๋๋ก์
๋๋ค. ์ฐจ์ด๊ฐ ์ฌ๋ผ์ง๋ ๊ฑด POD / ์์ ๊ฐ์ฒด์์ ์ปดํ์ผ๋ฌ ์ต์ ํ๋ก ์์ ๊ฐ์ฒด๊ฐ ์ ๊ฑฐ๋ ๋์ด๋ฉฐ โ reserve ๊ฐ ์๋๋ผ ์ต์ ํ ๋๋ถ์
๋๋ค.
reserve ๊ฐ ํธ์ถ๋ ๋ ๊ธฐ์กด ์์๊ฐ ์ด๋ํ๋๊ฐ?
reserve ํธ์ถ ์์ ๊ณผ ์ดํ๋ฅผ ๋๋ ์ผ ํฉ๋๋ค. reserve(n) ์ ํธ์ถํ ๋ ํ์ฌ capacity ๋ณด๋ค ํฌ๋ฉด ์ ๋ธ๋ก์ ํ ๋นํ๊ณ ๊ธฐ์กด ์์๋ฅผ ์ด๋ํ ๋ค ๊ธฐ์กด ๋ธ๋ก์ ํด์ ํฉ๋๋ค โ ์ด๋์ด 1ํ ๋ฐ์ํฉ๋๋ค. ์ดํ push_back ์ capacity ๋ฅผ ์ด๊ณผํ๊ธฐ ์ ๊น์ง ์ฌ๋กฏ์ ์ฝ์
๋ง ํ๋ฏ๋ก ์ด๋์ด ์์ต๋๋ค. ๊ฒฐ๊ตญ reserve ์ ํจ๊ณผ๋ โ์ฌ๋ฌ ๋ฒ ์ด๋ํ ๊ฑธ ์ต๋ 1ํ๋ก ์ค์ด๋ ๊ฒโ ์
๋๋ค.
ํต์ฌ ๊ฐ๋
| ๋ถ๋ฅ | ํค์๋ | ํ ์ค ์ ์ |
|---|---|---|
| vector ๊ด์ฉ๊ตฌ | push_back(value) | ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ ๋ณต์ฌยท์ด๋ํด ์ถ๊ฐ |
| ย | emplace_back(args...) | ์ฌ๋กฏ ์์์ ์์ฑ์ ์ง์ ํธ์ถ (in-place) |
| ย | perfect forwarding | ๊ฐ๋ณ ํ
ํ๋ฆฟ + std::forward ๋ก ์ธ์ ๊ทธ๋๋ก ์ ๋ฌ |
| ย | rvalue ์ค๋ฒ๋ก๋ | C++11 ์ push_back(T&&) โ ์์ ๊ฐ์ฒด ์ด๋ |
| ย | explicit ์์ฑ์ | emplace_back ์ผ๋ก ์ฐํ ๊ฐ๋ฅ โ ์ฃผ์ |
| ๋ฉ๋ชจ๋ฆฌ | size() | ํ์ฌ ์์ ์ |
| ย | capacity() | ์ฌํ ๋น ์์ด ๋ด์ ์ ์๋ ์ต๋ ์์ ์ |
| ย | ์ฌํ ๋น (Reallocation) | size > capacity ์ ์ ๋ฉ๋ชจ๋ฆฌ + ์ ์ฒด ๋ณต์ฌยท์ด๋ |
| ย | amortized O(1) | ํ๊ท ์ O(1), ์ต์ (์ฌํ ๋น) ์ O(n) โ ํ๊ท ํํ๋ฉด O(1) |
| ย | growth factor | ๋ณดํต 1.5x ~ 2x (๊ตฌํ๋ง๋ค ๋ค๋ฆ) |
| ย | reserve(n) | ์ฌํ ๋น ๋ฏธ๋ฆฌ 1ํ โ ์ดํ n ๊ฐ๊น์ง ์ฌํ ๋น 0 |
| ย | shrink_to_fit() | capacity ๋ฅผ size ๋ก ์ถ์ (๊ตฌํ ๊ถ๊ณ ) |
| ํด์ ์ถฉ๋ | ์ถฉ๋ (Collision) | ๋ค๋ฅธ ํค๊ฐ ๊ฐ์ ๋ฒํท ์ธ๋ฑ์ค |
| ย | ์ฒด์ด๋ (Separate Chaining) | ๊ฐ์ ๋ฒํท์ ์์๋ฅผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๋ฌถ์ |
| ย | ์คํ ์ด๋๋ ์ฑ (Open Addressing) | ์ถฉ๋ ์ ๋ค์ ๋น ์ฌ๋กฏ์ผ๋ก probing |
| ย | ์ ํ ํ์ฌ (Linear Probing) | ์ถฉ๋ ์ +1 ์นธ์ฉ ์ด๋ |
| ย | ์ด์ฐจ ํ์ฌ (Quadratic Probing) | ์ถฉ๋ ์ +1ยฒ, +2ยฒ, +3ยฒ โฆ ์ด๋ |
| ย | ์ด์ค ํด์ฑ (Double Hashing) | ๋ ๋ฒ์งธ ํด์ ํจ์์ ๊ฒฐ๊ณผ๋งํผ ์ด๋ |
| ย | ๋ก๋น ํ๋ ํด์ฑ (Robin Hood) | probing ๊ฑฐ๋ฆฌ ๊ท ๋ฑํ |
| ย | ํด๋ฌ์คํฐ๋ง | open addressing ์ ์ถฉ๋์ด ์ธ์ ์ฌ๋กฏ์ ๋ชฐ๋ฆผ |
๋ชฉ์ฐจ
- push_back vs emplace_back โ ํต์ฌ ์ฐจ์ด
- ๋ด๋ถ ๋์ โ ๊ฐ๋ณ ํ ํ๋ฆฟ + perfect forwarding
- ์ฑ๋ฅ ์ฐจ์ด๊ฐ ๊ฒฐ์ ์ ์ธ ์ผ์ด์ค
- emplace_back ์ ํจ์
- vector capacity ์ ์ฌํ ๋น (rehash ์ ๋น๊ต)
- ํด์ ์ถฉ๋ ์ฒ๋ฆฌ โ ์ฒด์ด๋
- ํด์ ์ถฉ๋ ์ฒ๋ฆฌ โ ์คํ ์ด๋๋ ์ฑ
- STL ยท Unreal ์ ์ถฉ๋ ํด๊ฒฐ ์ ์ฑ
- ๋ฉด์ ๋จ๊ณจ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ
- ํ๊ท ๋ค๋ฆฌ
1. push_back vs emplace_back โ ํต์ฌ ์ฐจ์ด
1-1. ํ ์ค ๋น๊ต
1
2
push_back(value) โ "์์ฑ๋ ๊ฐ์ฒด๋ฅผ ์ค๊ฒ, ๊ฑฐ๊ธฐ์ ์ฎ๊ฒจ ๋ฃ์ด๋ผ"
emplace_back(args...) โ "์ฌ๋ฃ๋ฅผ ์ค๊ฒ, ๊ฑฐ๊ธฐ์ ์ง์ ๋ง๋ค์ด๋ผ"
1-2. ๋์ ๋จ๊ณ ๋น๊ต
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point { int x, y; Point(int x, int y) : x(x), y(y) {} };
std::vector<Point> v;
// push_back โ 2๋จ๊ณ
v.push_back(Point(1, 2));
// 1) Point(1, 2) ์์ ๊ฐ์ฒด ์์ฑ (์คํ ๋๋ ์์ ์์ญ)
// 2) v ์ ์ฌ๋กฏ์ผ๋ก ์ด๋ ์์ฑ (move ctor)
// โ ์์ ๊ฐ์ฒด 1๊ฐ + ์ด๋ 1๋ฒ
// emplace_back โ 1๋จ๊ณ
v.emplace_back(1, 2);
// 1) v ์ ์ฌ๋กฏ์์ Point(1, 2) ์ง์ ์์ฑ (placement new ํจ๊ณผ)
// โ ์์ ๊ฐ์ฒด 0๊ฐ, ์ด๋ 0๋ฒ โ
1-3. ์๊ทธ๋์ฒ
1
2
3
4
5
6
7
// push_back โ 2๊ฐ ์ค๋ฒ๋ก๋
void push_back(const T& value); // lvalue: ๋ณต์ฌ
void push_back(T&& value); // rvalue: ์ด๋ (C++11)
// emplace_back โ ๊ฐ๋ณ ํ
ํ๋ฆฟ 1๊ฐ
template<class... Args>
T& emplace_back(Args&&... args); // perfect forwarding (C++11)
1-4. ๋ฌด์์ ๋ฐ๋๊ฐ
| ย | push_back | emplace_back |
|---|---|---|
| ๋ฐ๋ ๊ฒ | ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด (T ๋๋ T&&) | ์์ฑ์ ์ธ์๋ค (Args...) |
| ํธ์ถ ์ | v.push_back(Point(1,2)) | v.emplace_back(1, 2) |
| ์์ ๊ฐ์ฒด | 1๊ฐ ์์ฑ + ์ด๋ | 0๊ฐ โ |
| ๋น์ฉ | ์์ฑ + ์ด๋ (๋๋ ๋ณต์ฌ) | ์์ฑ 1๋ฒ |
2. ๋ด๋ถ ๋์ โ ๊ฐ๋ณ ํ ํ๋ฆฟ + perfect forwarding
2-1. emplace_back ์ ๊ตฌํ ๊ณจ๊ฒฉ (๋จ์ํ)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<class T, class Alloc>
template<class... Args>
T& vector<T,Alloc>::emplace_back(Args&&... args) {
if (size_ == capacity_) {
grow(); // ์ฌํ ๋น
}
// ์ฌ๋กฏ์์ ์ง์ ์์ฑ โ placement new ์ ๊ฐ์ ํจ๊ณผ
std::allocator_traits<Alloc>::construct(
alloc_, data_ + size_,
std::forward<Args>(args)... // perfect forwarding
);
++size_;
return data_[size_ - 1];
}
ํต์ฌ ๋ ๊ฐ์ง:
- ๊ฐ๋ณ ํ
ํ๋ฆฟ (
Args...) โ ์์ ๊ฐ์์ ์ธ์๋ฅผ ๊ทธ๋๋ก ๋ฐ์ std::forward<Args>(args)...โ lvalue/rvalue ์ ๋ณด๋ฅผ ๋ณด์กดํด ์์ฑ์์ ์ ๋ฌ (perfect forwarding)
2-2. ์ T&& ๊ฐ ์๋๋ผ Args&&... ์ธ๊ฐ
1
2
3
4
5
6
7
// ๋ง์ฝ ์ด๋ ๊ฒ ํ๋ค๋ฉด:
void emplace_back(T&& temp); // โ ์ด๋ฌ๋ฉด push_back ๊ณผ ๋ค๋ฅผ ๊ฒ ์์
// ๊ฐ๋ณ ํ
ํ๋ฆฟ์ด๋ผ์ผ ๋น๋ก์:
v.emplace_back(1, 2, 3); // ์ธ์ 3๊ฐ โ T(1, 2, 3) ์ง์ ์์ฑ
v.emplace_back(); // ์ธ์ 0๊ฐ โ T() ์ง์ ์์ฑ
v.emplace_back("Alice", 30); // ์ธ์ 2๊ฐ โ T("Alice", 30) ์ง์ ์์ฑ
์ฌ๋ฌ ์ธ์๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํด ์์ T ๋ฅผ ๋ง๋ค์ง ์๊ณ ์ฌ๋กฏ์์ ๋ฐ๋ก ์์ฑ์๋ฅผ ํธ์ถํ๋ ๊ฒ emplace ์ ๋ณธ์ง.
2-3. perfect forwarding ์ ์๋ฏธ
1
2
3
4
5
std::string s = "Alice";
v.emplace_back(s); // s ๋ lvalue โ string(const string&) ๋ณต์ฌ
v.emplace_back(std::move(s)); // rvalue โ string(string&&) ์ด๋
v.emplace_back("Alice"); // rvalue (const char*) โ string(const char*) ์ง์
std::forward<Args>(args)... ๋ ํธ์ถ์๊ฐ lvalue ๋ก ์คฌ๋์ง rvalue ๋ก ์คฌ๋์ง๋ฅผ ๊ทธ๋๋ก ์์ฑ์์ ์ ๋ฌํ๋ค. ์ด ๋๋ถ์ emplace_back ํ ํจ์๋ก ๋ณต์ฌยท์ด๋ยท์ง์ ์์ฑ์ ๋ชจ๋ ์ปค๋ฒํ๋ค.
3. ์ฑ๋ฅ ์ฐจ์ด๊ฐ ๊ฒฐ์ ์ ์ธ ์ผ์ด์ค
3-1. ๋ฌด๋ธ-์จ๋ฆฌ ํ์
(unique_ptr)
1
2
3
4
5
6
7
8
9
std::vector<std::unique_ptr<Resource>> v;
// push_back โ std::unique_ptr ์์ ๋ง๋ค๊ณ ์ด๋
v.push_back(std::make_unique<Resource>("data"));
// ๋๋: v.push_back(std::unique_ptr<Resource>(new Resource("data")));
// emplace_back โ ์ฌ๋กฏ์์ unique_ptr ์ง์ ์์ฑ
v.emplace_back(new Resource("data")); // ์ฃผ์: raw new โ exception unsafe
v.emplace_back(std::make_unique<Resource>("data")); // ์์ + in-place
๋ ๋ฒ์งธ ์ค์ raw
new๋ฅผ ์ฌ์ฉํด make_unique ์ emplace_back ์ฌ์ด์ ์์ธ ๋ฐ์ ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ์ํ์ด ์๋ค. C++14 ์ดํ๋ก๋std::make_unique+emplace_back๋๋push_back๋ ์ค ๋ฌด์์ ์จ๋ ๋๋ฑํ๊ฒ ์์ .
3-2. ํฐ ๊ฐ์ฒด์ ๋ณต์กํ ์์ฑ์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct BigObject {
BigObject(int a, double b, const std::string& c, std::vector<int> d) { /*...*/ }
};
std::vector<BigObject> v;
// push_back โ BigObject ์์ ๊ฐ์ฒด ์์ฑ ํ ์ด๋
v.push_back(BigObject(1, 2.0, "x", {1,2,3}));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ์ด ์์ ๊ฐ์ฒด๊ฐ ์คํ์ ์ ๊น ๋ง๋ค์ด์ก๋ค๊ฐ ์ฌ๋กฏ์ผ๋ก ์ด๋
// emplace_back โ ์ฌ๋กฏ ์์์ ์ง์ ์์ฑ
v.emplace_back(1, 2.0, "x", std::vector<int>{1,2,3});
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ์ด ์ธ์๋ค์ด ๊ทธ๋๋ก BigObject ์์ฑ์๋ก ์ ๋ฌ๋จ
์ด๋ ์์ฑ์๊ฐ trivial ์ด๋ผ๋ ์์ ๊ฐ์ฒด ์์ฒด์ ์คํ ๊ณต๊ฐ + ์ ๋ฆฌ ๋น์ฉ์ด ์๋ค. ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด์ผ์๋ก ์ฐจ์ด๊ฐ ์ปค์ง.
3-3. POD / ์์ ๊ฐ์ฒด๋ ์ฐจ์ด ์์
1
2
3
4
5
std::vector<int> v;
v.push_back(42); // int ํ ๋ฒ ๋ณต์ฌ โ 1 cycle
v.emplace_back(42); // int ํ ๋ฒ ์์ฑ โ 1 cycle
// ์ปดํ์ผ๋ฌ ์ต์ ํ ํ ๋ ์ฝ๋๋ ๋ณดํต ๋์ผํ ์ด์
๋ธ๋ฆฌ
int, float, ์์ POD ๊ตฌ์กฐ์ฒด ๋ฑ์ ๋ ํจ์๊ฐ ์ฌ์ค์ ๊ฐ๋ค. ์๋ฏธ๋ง ์ฐจ์ด๋ ๋ฟ ์ฑ๋ฅ์ ๊ฐ์.
3-4. ์ ๋ฆฌํ
| ์ผ์ด์ค | push_back | emplace_back | ์ฐจ์ด |
|---|---|---|---|
POD (int, float) | 1 ๋จ๊ณ | 1 ๋จ๊ณ | ์์ |
์์ ๊ฐ์ฒด (std::pair<int,int>) | ์์ + ์ด๋ | ์ง์ ์์ฑ | ๋ฏธ๋ฏธ |
| ํฐ ๊ฐ์ฒด (๊ธด ๋ฌธ์์ดยท์ปจํ ์ด๋ ๋ฉค๋ฒ) | ์์ + ์ด๋ | ์ง์ ์์ฑ | ์์ |
๋ฌด๋ธ-์จ๋ฆฌ (unique_ptr) | ์์ + ์ด๋ | ์ง์ ์์ฑ | ์์ |
| explicit ์์ฑ์ | ๋ช ์ ๋ณํ ํ์ | ์๋ ํธ์ถ (์ํ!) | ํจ์ ยง4 |
4. emplace_back ์ ํจ์
4-1. explicit ์์ฑ์ ์ฐํ
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Wrapper {
explicit Wrapper(int x) : x(x) {} // explicit โ ์์์ ๋ณํ ๊ธ์ง
int x;
};
std::vector<Wrapper> v;
// push_back โ explicit ๊ฐ์ โ ๋ช
์์ ๋ณํ ํ์
v.push_back(42); // โ ์ปดํ์ผ ์๋ฌ
v.push_back(Wrapper(42)); // โ
๋ช
์์
// emplace_back โ explicit ์ฐํ (์์ฑ์ ์ธ์๋ก ์ ๋ฌ๋๋ฏ๋ก)
v.emplace_back(42); // โ
์ปดํ์ผ OK โ ์๋ํ ๊ฒ ๋ง๋?
explicit ์ ์๋๋ ์์์ ๋ณํ ๊ธ์ง์ธ๋ฐ emplace_back ์ ๊ทธ๊ฑธ ์ฐํํ๋ค. ๊ฐ์ ๋ณํ์ ํ๊ณ ์ถ์๋ค๋ฉด OK, ์ค์์๋ค๋ฉด ๋ฒ๊ทธ.
4-2. ์ฝ๋ ์๋ ํ๋ฆผ
1
2
v.push_back(SomeType(arg1, arg2)); // ํ์
์ด ๋ณด์ โ ์๋ ๋ช
ํ
v.emplace_back(arg1, arg2); // ์ด๋ค ํ์
์ด ๋ง๋ค์ด์ง๋์ง ํค๋๋ฅผ ๋ด์ผ ์ ์ ์์
๋ฆฌ๋ทฐยท๋๋ฒ๊น
์ ๊ฐ๋
์ฑ์ด ๋จ์ด์ง๋ค. ํ์
์ด ๋ช
ํํ์ง ์์ ์๋ฆฌ(std::variant, ํ
ํ๋ฆฟ ์ปจํ
์คํธ) ์์๋ ๋ ํท๊ฐ๋ฆผ.
4-3. ์ธ์ ๊ฐ์ ๋ถ์ผ์น ์ ์ปดํ์ผ ์๋ฌ ๋ฉ์์ง
1
2
3
4
5
6
struct Point { Point(int, int); };
std::vector<Point> v;
v.emplace_back(1); // ์ปดํ์ผ ์๋ฌ โ Point(int) ์์
// ์๋ฌ ๋ฉ์์ง๊ฐ "no matching constructor for ..." ๊ฐ์ ๊น์ ํ
ํ๋ฆฟ ์ค๋ฅ๋ก ๋์ด
// push_back ์ด๋ผ๋ฉด "no matching function for push_back" ์ผ๋ก ๋ ์ง๊ด์
4-4. ๋๋ฑ ํจ์จ์ผ ๋๋ push_back ๊ถ์ฅ
โํ์คํ ์ด๋์ด ์์ผ๋ฉด push_backโ โ Effective Modern C++ Item 42 ์ ๊ถ๊ณ . ๊ฐ๋ ์ฑ + ์ปดํ์ผ ์๋ฌ ๋ช ํ์ฑ + explicit ์์ ์ฑ ๋ชจ๋ push_back ์ฐ์.
emplace ๋ ๋ฌด๋ธ-์จ๋ฆฌ ํ์ / ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด / in-place ์์ฑ์ด ๋ณธ์ง์ ์ธ ์๋ฆฌ ์์๋ง ์ ํ.
5. vector capacity ์ ์ฌํ ๋น (rehash ์ ๋น๊ต)
ํต์ฌ ํ ๋ฌธ์ฅ
vector๋size > capacity๊ฐ ๋๋ ์๊ฐ ์ ๋ฐฐ์ด ํ ๋น + ์ ์ฒด ๋ณต์ฌยท์ด๋ + ๊ธฐ์กด ๋ฐฐ์ด ํด์ ๋ฅผ ์ํํ๋ค. ์ด๊ฒ unordered_map ์ rehash ์ ๋์ผํ ํจํด์ด๋ฉฐ, iteratorยทํฌ์ธํฐยท์ฐธ์กฐ ๋ชจ๋ ๋ฌดํจํ์ํจ๋ค.
5-1. size vs capacity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::vector<int> v;
std::cout << v.size() << " / " << v.capacity() << "\n"; // 0 / 0
v.push_back(1);
std::cout << v.size() << " / " << v.capacity() << "\n"; // 1 / 1 (๊ตฌํ๋ง๋ค ๋ค๋ฆ)
v.push_back(2); // ์ฌํ ๋น ๋ฐ์ (size > capacity)
std::cout << v.size() << " / " << v.capacity() << "\n"; // 2 / 2
v.push_back(3); // ์ฌํ ๋น ๋ฐ์
std::cout << v.size() << " / " << v.capacity() << "\n"; // 3 / 4
v.push_back(4); // ์ฌํ ๋น ์์ (capacity ์ฌ์ )
std::cout << v.size() << " / " << v.capacity() << "\n"; // 4 / 4
v.push_back(5); // ์ฌํ ๋น ๋ฐ์
std::cout << v.size() << " / " << v.capacity() << "\n"; // 5 / 8
size()โ ์ค์ ๋ค์ด์๋ ์์ ์capacity()โ ์ฌํ ๋น ์์ด ๋ด์ ์ ์๋ ์ต๋ ์์ ์capacity > size๊ฐ ์ ์ โ ์ฌ์ ๋ถ์ด ๋ค์ push_back ์ ์ฌํ ๋น์ ๋ง์์ค
5-2. ์ฌํ ๋น ๋์
1
2
3
4
5
6
7
8
9
์ฌํ ๋น ์๊ณ ๋ฆฌ์ฆ:
1) ์ ๋ฐฐ์ด ํ ๋น (๋ณดํต capacity ร 2 ๋๋ ร 1.5)
2) ๊ธฐ์กด ์์๋ค์ ์ ๋ฐฐ์ด๋ก ์ด๋(๋๋ ๋ณต์ฌ) ์์ฑ
3) ๊ธฐ์กด ๋ฐฐ์ด์ ์์๋ค ์๋ฉธ์ ํธ์ถ
4) ๊ธฐ์กด ๋ฐฐ์ด ๋ฉ๋ชจ๋ฆฌ ํด์
5) data_ ํฌ์ธํฐยทcapacity_ ๊ฐฑ์
๋น์ฉ: O(n) โ ๋ชจ๋ ์์ ์ด๋
๋ถ์ ํจ๊ณผ: ๋ชจ๋ iteratorยทํฌ์ธํฐยท์ฐธ์กฐ ๋ฌดํจํ โ
5-3. growth factor โ ์ 1.5x ๋๋ 2x?
1
2
3
4
5
2๋ฐฐ ์ฑ์ฅ (gcc, clang ์ผ๋ฐ):
capacity ์งํ: 0 โ 1 โ 2 โ 4 โ 8 โ 16 โ ...
1.5๋ฐฐ ์ฑ์ฅ (MSVC):
capacity ์งํ: 0 โ 1 โ 2 โ 3 โ 4 โ 6 โ 9 โ 13 โ ...
| ย | 2x | 1.5x |
|---|---|---|
| ์ฌํ ๋น ํ์ | ์ ์ (logโ n) | ์ฝ๊ฐ ๋ ๋ง์ |
| ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น | ์ต๋ 50% | ์ต๋ 33% |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฌ์ฉ | ์ด๋ ค์ (์ด์ ๋ธ๋ก๋ณด๋ค ํฌ๋ ๋ชป ๋ค์ด๊ฐ) | ๊ฐ๋ฅ (์์ ๋ธ๋ก๋ค ํฉ์น๋ฉด ์ ํฌ๊ธฐ ๊ฐ๋ฅ) |
amortized O(1) โ ๋งค push_back ์ ํ๊ท ๋น์ฉ์ด O(1) ์ด๋ผ๋ ๋ณด์ฅ. ๊ฐ๋ O(n) ์ฌํ ๋น์ด ์ผ์ด๋๋ ์์ฃผ ์ผ์ด๋์ง ์์ผ๋ฏ๋ก ํ๊ท ํํ๋ฉด ์์ ์๊ฐ.
5-4. reserve ๋ก ์ฌํ ๋น ํํผ
1
2
3
4
5
6
7
std::vector<int> v;
v.reserve(10000); // capacity ๋ฅผ 10000 ์ผ๋ก ๋ฏธ๋ฆฌ ํ๋ณด (size ๋ 0)
for (int i = 0; i < 10000; ++i)
v.push_back(i); // ์ฌํ ๋น 0ํ โ ์ด๋ฏธ capacity ์ถฉ๋ถ
// ๋น๊ต: reserve ์์ด 10000๋ฒ push_back ํ๋ฉด logโ(10000) โ 14๋ฒ ์ฌํ ๋น
์ฑ๋ฅ ๊ฒฐ์ ์ ์ฝ๋๋ ์์ ํฌ๊ธฐ๋ฅผ ์๋ฉด reserve ๊ฐ ํ์ค. unordered_map ์ u.reserve(n) ๊ณผ ๊ฐ์ ์ญํ .
5-5. vector ์ฌํ ๋น vs unordered_map rehash โ ๋ฐ์นผ์ฝ๋ง๋
| ย | vector ์ฌํ ๋น | unordered_map rehash |
|---|---|---|
| ํธ๋ฆฌ๊ฑฐ | size > capacity | load_factor > max_load_factor |
| ๋์ | ์ ๋ฐฐ์ด + ์ ์ฒด ์ด๋ + ํด์ | ์ ๋ฒํท + ์ ์ฒด ์ฌ๋ฐฐ์น + ํด์ |
| ๋น์ฉ | O(n) | O(n) |
| ํ๊ท ๋ถํ ์ํ | O(1) (push_back) | O(1) (insert) |
| iterator ๋ฌดํจํ | ๋ชจ๋ | ๋ชจ๋ |
| ํํผ | reserve(n) | reserve(n) |
๋ ๋ค โ์ฉ๋ ์ด๊ณผ ์ ์๋ก ํ ๋นํด ํต์งธ๋ก ์ฎ๊ธฐ๋ ํจํดโ. STL ์ ๋ถํ ์ํ ์๋ฃ๊ตฌ์กฐ๊ฐ ๊ณต์ ํ๋ ํจํด์ด๊ณ , ์ด ๋๋ฌธ์ iterator ๋ณด๊ด ์ ํญ์ ์ํ. 14๋ฒ ยง12 ์ ๊ฐ์ ๋งฅ๋ฝ.
5-6. shrink_to_fit
1
2
3
std::vector<int> v(1000);
v.resize(10); // size = 10, capacity = 1000 (๋ญ๋น)
v.shrink_to_fit(); // capacity ๋ฅผ size ๋ก ์ถ์ ์๋ (๊ตฌํ ๊ถ๊ณ )
shrink_to_fit ์ ๊ถ๊ณ ์ผ ๋ฟ ๋ณด์ฅ์ด ์๋๋ค. ์ผ๋ถ ๊ตฌํ์ ๋ฌด์ํ ์ ์์. ์ค์ ๋ก ๋ฉ๋ชจ๋ฆฌ ํ์๊ฐ ํ์ํ๋ฉด swap idiom (std::vector<T>(v).swap(v)) ์ฌ์ฉ.
6. ํด์ ์ถฉ๋ ์ฒ๋ฆฌ โ ์ฒด์ด๋ (Separate Chaining)
ํต์ฌ ํ ๋ฌธ์ฅ
์ฒด์ด๋์ ๊ฐ์ ๋ฒํท์ ๋งคํ๋ ์์๋ค์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ(๋๋ ์์ ์ปจํ ์ด๋) ๋ก ๋ฌถ๋ ๋ฐฉ์์ด๋ค. STL
std::unordered_map์ด ์ฑํํ ํ์ค์ ๋ฐฉ์.
6-1. ๋์
1
2
3
4
5
6
hash("foo") % 16 = 5
hash("xyz") % 16 = 5 โ ์ถฉ๋
hash("abc") % 16 = 5 โ ๋ ์ถฉ๋
buckets[5] โโ [foo, 100] โโ [xyz, 200] โโ [abc, 300] โโ nullptr
(์ฒด์ธ ๋
ธ๋ 1) (์ฒด์ธ ๋
ธ๋ 2) (์ฒด์ธ ๋
ธ๋ 3)
๊ฐ ๋ฒํท ์ฌ๋กฏ์ด ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ํค๋ ํฌ์ธํฐ๋ฅผ ๋ค๊ณ ์๊ณ , ์ถฉ๋ ์ ์ ๋ ธ๋๋ฅผ ๊ทธ ๋ฆฌ์คํธ์ ์ถ๊ฐํ๋ค.
6-2. ๊ตฌํ ๊ณจ๊ฒฉ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Node {
Key key;
Value value;
Node* next;
};
std::vector<Node*> buckets; // ๋ฒํท ๋ฐฐ์ด (๊ฐ ์นธ์ด ๋ฆฌ์คํธ ํค๋)
void insert(Key k, Value v) {
size_t idx = hash(k) % buckets.size();
Node* n = new Node{k, v, buckets[idx]}; // ์ ๋
ธ๋๋ฅผ ํค๋๋ก
buckets[idx] = n;
}
Value* find(Key k) {
size_t idx = hash(k) % buckets.size();
for (Node* n = buckets[idx]; n; n = n->next) {
if (n->key == k) return &n->value;
}
return nullptr;
}
6-3. ์๊ฐ ๋ณต์ก๋
| ์ฐ์ฐ | ํ๊ท (๊ท ๋ฑ ๋ถํฌ) | ์ต์ (๋ชจ๋ ํค ์ถฉ๋) |
|---|---|---|
| insert | O(1) | O(n) |
| find | O(1) | O(n) |
| erase | O(1) | O(n) |
ํ๊ท ์ ์ฒด์ธ ๊ธธ์ด โ load_factor ๋ผ์ load_factor ๊ฐ 1.0 ์ด๋ฉด ํ๊ท 1ํ ๋น๊ต๋ก ๋.
6-4. ์ฅ์
- ๊ตฌํ ๊ฐ๋จ โ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ์ถ๊ฐ/์ญ์ ๋ง ์๋ฉด ๋จ
- load_factor 1.0 ์ด๊ณผํด๋ ๋์ (์ฑ๋ฅ ์ ํ๋ง)
- ์ญ์ ๋จ์ โ ๋ ธ๋ ํ ๊ฐ๋ง ์ ๊ฑฐํ๋ฉด ๋
- iterator ์์ ์ฑ (๊ฐ๋ณ ๋ ธ๋ ๋จ์) โ rehash ๋ง ์๋๋ฉด ๋ ธ๋ ์์
6-5. ๋จ์
- ๋ ธ๋ ๋ถ์ฐ โ ํ์ ๋ฐ๋ก ํ ๋น, ์บ์ ์ ๋์
- ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋ โ ๋ ธ๋๋ง๋ค next ํฌ์ธํฐ + ํ ํค๋
- ํฌ์ธํฐ ์ถ์ ๋น์ฉ โ ์บ์ ๋ฏธ์ค ๊ฐ๋ฅ์ฑ
์ด ๋จ์ ๋ค ๋๋ฌธ์ ๊ฒ์ ์์ง์์๋ ํํผ โ Unreal TMap ์ด ๋ค๋ฅธ ๋ฐฉ์์ ์ฐ๋ ์ด์ .
7. ํด์ ์ถฉ๋ ์ฒ๋ฆฌ โ ์คํ ์ด๋๋ ์ฑ (Open Addressing)
ํต์ฌ ํ ๋ฌธ์ฅ
์คํ ์ด๋๋ ์ฑ์ ์ถฉ๋ ์ ๊ฐ์ ๋ฒํท ๋ฐฐ์ด ์์์ ๋ค์ ๋น ์ฌ๋กฏ์ ์ฐพ์ ๊ฐ๋ ๋ฐฉ์์ด๋ค. ๋ ธ๋๋ฅผ ๋ฐ๋ก ํ ๋นํ์ง ์๊ณ ๋ฐฐ์ด ํ ๋ฉ์ด๋ฆฌ ๋ง ์ฌ์ฉํด ์บ์ ์นํ์ .
7-1. ๊ธฐ๋ณธ ์์ด๋์ด
1
2
3
4
5
6
hash("foo") % 16 = 5 โ buckets[5] ์ ์ ์ฅ
hash("xyz") % 16 = 5 โ buckets[5] ์ฐจ์์ โ buckets[6] ์๋ โ ๋น์ด์์ผ๋ฉด ๊ฑฐ๊ธฐ
buckets: [_, _, _, _, _, foo, xyz, _, _, _, ...]
โ โ
์๋ ์์น probing ์ผ๋ก ์ด๋
์ฒด์ด๋๊ณผ ๋ฌ๋ฆฌ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๊ฐ์ ๋ฐฐ์ด ์์ ์๋ค.
7-2. probing ์ ๋ต 3๊ฐ์ง
(1) ์ ํ ํ์ฌ (Linear Probing)
1
2
3
4
์ถฉ๋ ์ +1 ์นธ์ฉ ์ด๋
hash(k) % N โ +1 โ +2 โ +3 โ ... ๋น ์ฌ๋กฏ๊น์ง
buckets[5] ์ฐจ์์ โ buckets[6] โ buckets[7] โ ...
- ์ฅ์ : ์บ์ ์นํ ์ต๊ฐ (์ธ์ ์ฌ๋กฏ)
- ๋จ์ : ํด๋ฌ์คํฐ๋ง (Primary Clustering) โ ์ถฉ๋์ด ์ธ์ ์ฌ๋กฏ์ ๋ชฐ๋ ค ๊ธธ์ด์ง
(2) ์ด์ฐจ ํ์ฌ (Quadratic Probing)
1
2
์ถฉ๋ ์ +1ยฒ, +2ยฒ, +3ยฒ, ... ์ด๋
hash(k) % N โ +1 โ +4 โ +9 โ +16 โ ...
- ์ฅ์ : ํด๋ฌ์คํฐ๋ง ์ํ
- ๋จ์ : load_factor 0.5 ์ด์์์ ๋ฌดํ ๋ฃจํ ๊ฐ๋ฅ (๋ชจ๋ ์ฌ๋กฏ์ ๋ชป ๋ฐฉ๋ฌธ)
(3) ์ด์ค ํด์ฑ (Double Hashing)
1
2
๋ ๋ฒ์งธ ํด์ ํจ์ hash2(k) ๋ก step ๊ฒฐ์
hash1(k) % N โ +hash2(k) โ +2*hash2(k) โ ...
- ์ฅ์ : ํด๋ฌ์คํฐ๋ง ๊ฑฐ์ ์์
- ๋จ์ : ํด์ ํจ์ 2๊ฐ ํ์, ๊ณ์ฐ ๋น์ฉ ์ฆ๊ฐ
7-3. ์๊ฐ ๋ณต์ก๋
| ์ฐ์ฐ | load_factor 0.5 | load_factor 0.9 | load_factor 1.0 |
|---|---|---|---|
| find | ~1.5ํ ๋น๊ต | ~5.5ํ ๋น๊ต | ๋ฌดํ ๋ฃจํ โ |
| insert | ~1.5ํ ๋น๊ต | ~5.5ํ ๋น๊ต | ๋ถ๊ฐ |
์คํ ์ด๋๋ ์ฑ์ load_factor < 1.0 ๊ฐ์ โ ๊ฐ๋ ์ฐจ๋ฉด ๋์ ์์ฒด๊ฐ ์ ๋จ. ๋ณดํต 0.7~0.75 ์๊ณ๋ก rehash.
7-4. ์ญ์ ์ ์ด๋ ค์ โ Tombstone
1
2
3
4
5
6
7
8
9
10
11
์ญ์ ์ ๊ทธ๋ฅ ๋น์๋ฒ๋ฆฌ๋ฉด probing ์ฌ์ฌ์ด ๋์ด์ง:
buckets: [_, _, _, _, _, foo, xyz, abc]
erase("xyz") โ buckets[6] = empty
buckets: [_, _, _, _, _, foo, _, abc]
โ
์ฌ๊ธฐ์ probing ๋๊น โ "abc" ๋ชป ์ฐพ์
ํด๊ฒฐ: tombstone (์ญ์ ํ์)
buckets: [_, _, _, _, _, foo, โซ, abc]
โ
๋น์ด์์ง๋ง probing ์ ํต๊ณผ
tombstone ์ ๋ณ๋ ๋นํธ ๋๋ sentinel ๊ฐ์ผ๋ก ํ์. ๋๋ฌด ๋ง์ด ์์ด๋ฉด rehash ๋ก ์ ๋ฆฌ.
7-5. ๋ก๋น ํ๋ ํด์ฑ (Robin Hood Hashing)
probing ๊ฑฐ๋ฆฌ๊ฐ ๋ ๊ธด ์์๋ฅผ ๋ง๋๋ฉด ์๋ฆฌ๋ฅผ ๋นผ์์ probing ๊ฑฐ๋ฆฌ๋ฅผ ๊ท ๋ฑํ. ํ๊ท + ์ต์ ๋ชจ๋ ์ต์ ์ ๋ถํฌ.
์ด๋ฆ์ ์ ๋: โ๋ถ์(์งง์ probing) ํํ ์ ๋นผ์์ ๊ฐ๋ํ(๊ธด probing) ์ฌ๋ํํ ์คโ
7-6. ์ฅ์ ยท๋จ์
์ฅ์
- ์บ์ ์นํ โ ๊ฐ์ ๋ฐฐ์ด ์์์ ๋ค์ ์ฌ๋กฏ์ด๋ผ prefetch ํจ๊ณผ
- ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋ ์์ โ next ํฌ์ธํฐ ๋ถํ์, ํ ํค๋ ์์
- ํ ๋น ํ์ ์ ์ โ ๋ฒํท ๋ฐฐ์ด 1๊ฐ๋ง
๋จ์
- load_factor ํ๊ณ โ 0.7~0.75 ๊ฐ์ (์ฒด์ด๋์ 1.0 ์ด๊ณผ๋ OK)
- ํด๋ฌ์คํฐ๋ง โ ์ถฉ๋์ด ์ธ์ ์ ๋ชฐ๋ฆผ (probing ์ ๋ต์ผ๋ก ์ํ)
- ์ญ์ ๋ณต์ก โ tombstone ๊ด๋ฆฌ ํ์
8. STL ยท Unreal ์ ์ถฉ๋ ํด๊ฒฐ ์ ์ฑ
8-1. ๋น๊ตํ
| ย | STL std::unordered_map | Unreal TMap |
|---|---|---|
| ์ถฉ๋ ํด๊ฒฐ | Separate Chaining โ | Open Addressing ๋ณํ โ |
| ์บ์ ์นํ | ๋ณดํต (์ฒด์ธ ๋ ธ๋ ๋ถ์ฐ) | ๋ ์ข์ (๋ฐฐ์ด ์ธ์ ) |
| ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋ | ๋ฒํท + ๋ ธ๋ next ํฌ์ธํฐ + ํ ํค๋ | ์ฌ๋กฏ ๋ฐฐ์ด + ์ฝ๊ฐ์ ๋ฉํ๋ฐ์ดํฐ |
| max_load_factor | 1.0 (๊ตฌํ ์ ์ ๊ฐ๋ฅ) | ์์ ๊ฐ (๊ตฌํ ์ ์) |
| ์ญ์ | ๋ ธ๋ ์ ๊ฑฐ | tombstone ๋๋ backshift |
| ํ ๋น ํ์ | ๋งค insert ๋ง๋ค ๋ ธ๋ 1๊ฐ | ๊ฑฐ์ ์์ (๋ฒํท ๋ฐฐ์ด 1๊ฐ) |
8-2. ์ STL ์ ์ฒด์ด๋์ธ๊ฐ
- ํ์ค ๋ช ์ธ์ iterator ์์ ์ฑ ์๊ตฌ์ฌํญ โ ์ฒด์ด๋์ด ๋ง์กฑ์ํค๊ธฐ ์ฌ์
- load_factor 1.0 ์ด๊ณผ ํ์ฉ โ ์ผ๋ถ ์ํฌ๋ก๋์์ ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
- ๊ตฌํ ๋จ์์ฑ โ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ ์ฑ ์ฐ์
8-3. ์ Unreal ์ ์คํ ์ด๋๋ ์ฑ์ธ๊ฐ
- ์บ์ ์นํ โ ๊ฒ์ frame budget (16.6ms) ์์์ lookup ๋น์ฉ ์ต์ํ
- ํ ๋น ํ์ โ โ ๋งค insert ๋ง๋ค
new์ ํจ โ GC + ๋จํธํ ๊ฐ์ - ๋ฉ๋ชจ๋ฆฌ ์ง์ญ์ฑ โ TArray + TMap + TSet ๋ชจ๋ ๊ฐ์ ์ฒ ํ (1๊ธ ์๋ฏผ)
- GC ํตํฉ โ UPROPERTY ์ ํจ๊ป ์๋, raw ํฌ์ธํฐ ๋ ธ๋ ํํผ
14๋ฒ ยง9-6 ๊ฒฐ๋ก ๊ณผ ๊ฐ์: โ๋ถ์ฐ ๋ ธ๋ ์๋ฃ๊ตฌ์กฐ ํํผโ ๊ฐ ๊ฒ์ ์์ง์ ์ผ๊ด๋ ์ฒ ํ.
9. ๋ฉด์ ๋จ๊ณจ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ
Q1. push_back ๊ณผ emplace_back ์ ์ฐจ์ด๋?
push_back ์ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๋ฐ์ ์ฌ๋กฏ์ผ๋ก ๋ณต์ฌยท์ด๋ํ๊ณ , emplace_back ์ ์ฌ๋กฏ ์์์ ๊ฐ๋ณ ํ ํ๋ฆฟ + perfect forwarding ์ผ๋ก ์ง์ ์์ฑํฉ๋๋ค. ๋ฌด๋ธ-์จ๋ฆฌ ํ์ ์ด๋ ํฐ ๊ฐ์ฒด์์ ์์ ๊ฐ์ฒด ์์ฑ ๋น์ฉ์ ์ค์ด๋ ๊ฒ emplace ์ ์์์ ๋๋ค.
Q2. ํญ์ emplace_back ์ด ๋ ๋น ๋ฅธ๊ฐ?
์๋๋๋ค. POD ๋ ์์ ๊ฐ์ฒด๋ ์ปดํ์ผ๋ฌ ์ต์ ํ๋ก ๋์ด ๊ฐ์ ์ฝ๋๊ฐ ๋ฉ๋๋ค. emplace ๋ explicit ์ฐํ, ํ์ ๋ชจํธ์ฑ, ์ปดํ์ผ ์๋ฌ ๋ฉ์์ง ๊ฐ๋ ์ฑ ๊ฐ์ ํจ์ ์ด ์์ด ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด๋ ๋ฌด๋ธ-์จ๋ฆฌ ํ์ ์์๋ง ๋ถ๋ช ํ ์ ๋ฆฌํฉ๋๋ค.
Q3. vector ์ capacity ๊ฐ size ์ ๋ค๋ฅธ ์ด์ ?
push_back ๋ง๋ค ์ฌํ ๋นํ๋ฉด ๋งค๋ฒ O(n) ์ด๋ผ ๋์ ๋น์ฉ์ด ์ปค์ง๋๋ค. capacity ๋ฅผ ๋ฏธ๋ฆฌ size ๋ณด๋ค ํฌ๊ฒ ์ก์๋๊ณ , ์ด๊ณผํ ๋๋ง ์ฌํ ๋นํ๋ฉด amortized O(1) ์ด ๋ฉ๋๋ค. ๋ณดํต 1.5~2๋ฐฐ ์ฑ์ฅ ์ ์ฑ ์ ์๋๋ค.
Q4. ์ฌํ ๋น ์ iterator ๋?
๋ชจ๋ ๋ฌดํจํ๋ฉ๋๋ค. ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ณต์ฌ๋ ํ ๊ธฐ์กด ๋ฉ๋ชจ๋ฆฌ๋ ํด์ ๋๋ฏ๋ก, ๊ธฐ์กด iteratorยทํฌ์ธํฐยท์ฐธ์กฐ๋ ๋๊ธ๋ง์ด ๋ฉ๋๋ค. 14๋ฒ unordered_map rehash ์ ๊ฐ์ ํจํด์ด๊ณ , reserve ๋ก ํํผํฉ๋๋ค.
Q5. ํด์ ์ถฉ๋์ด ๋ฐ์ํ๋ฉด?
ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง์
๋๋ค. ์ฒด์ด๋(Separate Chaining) ์ ๊ฐ์ ๋ฒํท์ ๋งคํ๋ ์์๋ค์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๋ฌถ๋ ๋ฐฉ์์ด๊ณ , STL std::unordered_map ์ด ์ฌ์ฉํฉ๋๋ค. ์คํ ์ด๋๋ ์ฑ(Open Addressing) ์ ๊ฐ์ ๋ฐฐ์ด ์์์ ๋ค์ ๋น ์ฌ๋กฏ์ผ๋ก probing ํ๋ ๋ฐฉ์์ด๊ณ , Unreal TMap ์ด ์ฌ์ฉํฉ๋๋ค. ์บ์ ์นํ์ฑ์ ์คํ ์ด๋๋ ์ฑ์ด ์ฐ์, load_factor ํ๊ณ๋ ์ฒด์ด๋์ด ์์ ๋ก์์
๋๋ค.
Q6. ์คํ ์ด๋๋ ์ฑ์ probing ์ข ๋ฅ?
์ ํ ํ์ฌ(+1 ์ฉ), ์ด์ฐจ ํ์ฌ(+1ยฒ, +2ยฒ, +3ยฒ ์ฉ), ์ด์ค ํด์ฑ(๋ ๋ฒ์งธ ํด์ ํจ์ step) ์ด ์์ต๋๋ค. ์ ํ์ ์บ์ ์นํ ์ต๊ฐ์ด์ง๋ง ํด๋ฌ์คํฐ๋ง์ด ์ฌํ๊ณ , ์ด์ค ํด์ฑ์ ๋ถํฌ๋ ์ข์ง๋ง ํด์ 2๊ฐ ๊ณ์ฐ ๋น์ฉ์ด ๋ญ๋๋ค. ๋ก๋น ํ๋ ํด์ฑ์ probing ๊ฑฐ๋ฆฌ๋ฅผ ๊ท ๋ฑํํ๋ ๋ณํ์ ๋๋ค.
Q7. ์คํ ์ด๋๋ ์ฑ์ ์ญ์ ๋ ์ด๋ป๊ฒ?
๊ทธ๋ฅ ๋น์ฐ๋ฉด probing ์ฌ์ฌ์ด ๋์ด์ ธ ๋ค์ ์์๋ฅผ ๋ชป ์ฐพ์ต๋๋ค. tombstone ์ผ๋ก โ์ญ์ ๋จโ ํ์๋ฅผ ๋๊ณ probing ์ ํต๊ณผํ๊ฒ ํฉ๋๋ค. tombstone ์ด ๋๋ฌด ์์ด๋ฉด rehash ๋ก ์ ๋ฆฌํฉ๋๋ค.
Q8. STL ์ ์ ์ฒด์ด๋, ์ธ๋ฆฌ์ผ์ ์ ์คํ ์ด๋๋ ์ฑ?
STL ์ ํ์ค ๋ช ์ธ์ iterator ์์ ์ฑ ์๊ตฌ์ load_factor 1.0 ์ด๊ณผ ํ์ฉ์ ์ํด ์ฒด์ด๋์ ํํ๊ณ , Unreal ์ ๋งค ํ๋ ์ 16.6ms ์์์ lookup ๋น์ฉ์ ์ค์ด๊ธฐ ์ํด ์บ์ ์นํ์ ์ธ ์คํ ์ด๋๋ ์ฑ + open addressing ๋ณํ์ ํํ์ต๋๋ค. โ๋ถ์ฐ ๋ ธ๋ ์๋ฃ๊ตฌ์กฐ ํํผโ ๋ผ๋ ๊ฒ์ ์์ง์ ์ผ๊ด๋ ์ฒ ํ์ ๋๋ค.
Q9. vector ์ฌํ ๋น๊ณผ ํด์ rehash ์ ๊ณตํต์ ?
๋ ๋ค ์ฉ๋ ์ด๊ณผ ์ ์๋ก ํ ๋นํด ํต์งธ๋ก ์ฎ๊ธฐ๋ ํจํด์ ๋๋ค. vector ๋ size > capacity, ํด์๋ load_factor > max_load_factor ๊ฐ ํธ๋ฆฌ๊ฑฐ์ด๊ณ , ๋ ๋ค O(n) ๋น์ฉ์ ๋ชจ๋ iterator ๋ฅผ ๋ฌดํจํํ๋ฉฐ, reserve ๋ก ํํผํ ์ ์์ต๋๋ค.
Q10. amortized O(1) ์ด๋?
๋งค ์ฐ์ฐ์ ํ๊ท ๋น์ฉ์ด O(1) ์ด๋ผ๋ ๋ณด์ฅ์ ๋๋ค. push_back 1๋ง ๋ฒ ์ค 14๋ฒ(2๋ฐฐ ์ฑ์ฅ ์ logโ 1๋ง) ๋ง ์ฌํ ๋น์ด๋ผ O(n) ์ด๊ณ ๋๋จธ์ง 9986๋ฒ์ O(1) ์ ๋๋ค. ํฉ์ฐํด์ 1๋ง์ผ๋ก ๋๋๋ฉด ํ๊ท ์ ์์. โ๊ฐ๋ ๋น์ผ ์ฐ์ฐ์ด ํ๊ท ์ผ๋ก ๋ฌปํ๋ ๋ถ์ ๊ธฐ๋ฒโ์ ๋๋ค.
10. ํ๊ท ๋ค๋ฆฌ
13_vector_vs_list.mdโ vector ์ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์ยท์บ์ ์นํ์ฑ. ยง5 capacity/์ฌํ ๋น ์ ์๋ณธ.14_std_map.mdโ std::map ์ RB-Tree, unordered_map rehash ์ ๋น๊ต. ยง5-5 ์ ๋ฐ์นผ์ฝ๋ง๋ ํ.14_std_map_followup.mdโ ๋ชจ์๋ฉด์ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ 16๊ฐ. ยง11 (ํด์ยท๋ฒํท), ยง12 (load_factor) ์ ๋ณธ ๋ฌธ์ ยง6ยทยง7 ๊ฐ ํ ์ธํธ.11_smart_pointer.mdโunique_ptr+ emplace_back ์์ ํจํด.10_pointer_deepdive.mdโ vector ์ฌํ ๋น ํ ๋๊ธ๋ง ํฌ์ธํฐ.
๋ชจ์๋ฉด์ ๋ต๋ณ ํ ํ๋ฆฟ (1๋ถ / 3๋ถ)
1๋ถ ๋ต๋ณ
push_back ์ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๋ฐ์ vector ์ ๋ง์ง๋ง ์ฌ๋กฏ์ ๋ณต์ฌยท์ด๋ํ๊ณ , emplace_back ์ ๊ฐ๋ณ ํ
ํ๋ฆฟ + perfect forwarding ์ผ๋ก ์ฌ๋กฏ ์์์ ์์ฑ์๋ฅผ ์ง์ ํธ์ถํฉ๋๋ค. ์์ ๊ฐ์ฒด ์์ฒด๊ฐ ์์ฑ๋์ง ์์ ๋ฌด๋ธ-์จ๋ฆฌ ํ์
์ด๋ ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด์์ ์ ๋ฆฌํฉ๋๋ค. ๋ค๋ง explicit ์์ฑ์๋ฅผ ์ฐํํ ์ ์๊ณ ํ์
์ ๋ช
์์ ์ผ๋ก ๋ณด์ฌ์ฃผ์ง ์์ ์๋๊ฐ ํ๋ ค์ง ์ ์์ด, ํ์คํ ์ด๋์ด ์์ผ๋ฉด push_back ์ด ๊ถ์ฅ๋ฉ๋๋ค.
3๋ถ ๋ต๋ณ (๊ผฌ๋ฆฌ๋ฌผ๊ธฐ ํฌํจ)
(์ 1๋ถ + ์ด์ด์)
vector ๋ size > capacity ๊ฐ ๋๋ ์๊ฐ ์ ๋ฐฐ์ด์ ํ ๋นํ๊ณ ์ ์ฒด๋ฅผ ์ฎ๊ธด ๋ค ๊ธฐ์กด์ ํด์ ํฉ๋๋ค. growth factor ๋ ๋ณดํต 1.5 ๋๋ 2๋ฐฐ์ด๋ฉฐ, ์ด ๋ถํ ์ํ์ผ๋ก push_back ์ด amortized O(1) ์ด ๋ฉ๋๋ค. ์ฌํ ๋น์ด ์ผ์ด๋๋ฉด ๋ชจ๋ iteratorยทํฌ์ธํฐยท์ฐธ์กฐ๊ฐ ๋ฌดํจํ๋๋ฏ๋ก, ํฌ๊ธฐ๋ฅผ ์๋ฉด reserve(n) ์ผ๋ก ๋ฏธ๋ฆฌ ํ๋ณดํ๋ ๊ฒ์ด ํ์ค์
๋๋ค.
์ด ํจํด์ unordered_map ์ rehash ์ ์ ํํ ๊ฐ์ต๋๋ค. unordered_map ์ load_factor > max_load_factor ์ผ ๋ ์ ๋ฒํท ๋ฐฐ์ด์ ํ ๋นํด ๋ชจ๋ ์์๋ฅผ ์ฌ๋ฐฐ์นํฉ๋๋ค. ํธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด๋ง ๋ค๋ฅผ ๋ฟ ๋น์ฉ O(n), iterator ์ ์ฒด ๋ฌดํจํ, reserve ๋ก ํํผ ๊ฐ๋ฅ โ ๋ชจ๋ ๋์ผํฉ๋๋ค.
ํด์ ์ถฉ๋์ด ๋ฐ์ํ ๋ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง์
๋๋ค. STL std::unordered_map ์ ๊ฐ์ ๋ฒํท์ ๋งคํ๋ ์์๋ค์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๋ฌถ๋ ์ฒด์ด๋(Separate Chaining) ์, Unreal TMap ์ ๊ฐ์ ๋ฐฐ์ด ์์์ ๋ค์ ๋น ์ฌ๋กฏ์ผ๋ก probing ํ๋ ์คํ ์ด๋๋ ์ฑ(Open Addressing) ์ ์ฌ์ฉํฉ๋๋ค. ๊ฒ์ ์์ง์ ๋งค ํ๋ ์ 16.6ms ์์์ lookup ๋น์ฉ์ ์ค์ด๊ธฐ ์ํด ์บ์ ์นํ์ ์ธ ์คํ ์ด๋๋ ์ฑ์ ํํ๊ณ , ์ด๋ vector โ TArray, list ํํผ ์ ๊ฐ์ ์ผ๊ด๋ ์ฒ ํ์
๋๋ค.