CS โ 1 vector vs hash concepts
๐ 15-1 โ push_back ยท emplace_back ยท ํด์ ๊ฐ๋ ์ง๋ฌธ ์ ๋ฆฌ
15_pushback_vs_emplaceback.md๋ฅผ ์ฝ์ผ๋ฉฐ ๋์จ ์ง๋ฌธ๋ค์ ๋ชจ์ ์ ๋ฆฌํ ๋ณด์ถฉ ํ์ผ.
Q1. explicit ์์ฑ์๋? placement new? ๊ฐ๋ณ ํ ํ๋ฆฟ ์ธ์ ์ ๋ฌ?
explicit ์์ฑ์
์์์ ๋ณํ์ ๋ง๋ ํค์๋. ์์ฑ์ ์์ ๋ถ์ด๋ฉด ๋ช ์์ ํธ์ถ๋ง ํ์ฉ.
1
2
3
4
5
6
7
struct Wrapper {
explicit Wrapper(int x) : x(x) {}
int x;
};
Wrapper w = 42; // โ ์์์ ๋ณํ ๊ธ์ง โ ์ปดํ์ผ ์๋ฌ
Wrapper w(42); // โ
๋ช
์์ ํธ์ถ๋ง ํ์ฉ
placement new
์ด๋ฏธ ํ๋ณด๋ ๋ฉ๋ชจ๋ฆฌ ์์ ์์ฑ์๋ฅผ ํธ์ถํ๋ ๋ฌธ๋ฒ. ์ ํ ํ ๋น ์์ด ์ฃผ์๋ง ์ง์ .
1
2
char buf[sizeof(Point)];
Point* p = new(buf) Point(1, 2); // buf ๋ฉ๋ชจ๋ฆฌ ์์์ Point(1,2) ์ง์ ์์ฑ
emplace_back ๋ด๋ถ์ allocator_traits::construct(alloc_, ptr, args...) ๊ฐ ์ ํํ ์ด ์ญํ . ์ฌ๋กฏ ์ฃผ์์ ์์ฑ์๋ฅผ ํธ์ถํ ๋ฟ, ํ ํ ๋น์ ์ฌํ ๋น ์์๋ง ์ผ์ด๋จ.
๊ฐ๋ณ ํ ํ๋ฆฟ ์ธ์ ์ ๋ฌ
Args&&... ๋ก ์์ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ์ std::forward<Args>(args)... ๋ก ์์ฑ์์ ๊ทธ๋๋ก ์ ๋ฌ.
1
2
3
4
5
6
7
8
9
10
template<class... Args>
T& emplace_back(Args&&... args) {
// ์ฌ๋กฏ ์์์ ์ง์ ์์ฑ
std::allocator_traits<Alloc>::construct(
alloc_, data_ + size_,
std::forward<Args>(args)... // lvalue/rvalue ์ ๋ณด ๋ณด์กดํด์ ์์ฑ์๋ก ์ ๋ฌ
);
++size_;
return data_[size_ - 1];
}
Q2. push_back ์ lvalue / rvalue
1
2
3
4
Point p(1, 2);
v.push_back(p); // lvalue โ const T& ์ค๋ฒ๋ก๋ โ ๋ณต์ฌ ์์ฑ์
v.push_back(Point(1, 2)); // rvalue (์์ ๊ฐ์ฒด) โ T&& ์ค๋ฒ๋ก๋ โ ์ด๋ ์์ฑ์
v.push_back(std::move(p)); // ๋ช
์์ rvalue โ T&& ์ค๋ฒ๋ก๋ โ ์ด๋ ์์ฑ์
C++11 ์ด์ ์๋ ๋ณต์ฌ๋ง ์์๊ณ , C++11 ์์ push_back(T&&) ์ค๋ฒ๋ก๋๊ฐ ์ถ๊ฐ๋๋ฉด์ ์์ ๊ฐ์ฒด๋ ์ด๋์ผ๋ก ์ฒ๋ฆฌ๋จ.
Q3. push_back(โAliceโ) ๋ ๊ฐ๋ณ ํ ํ๋ฆฟ ์๋๊ฐ?
์๋. push_back ์๊ทธ๋์ฒ๋ push_back(T&&) โ ์ธ์ 1๊ฐ๋ง ๋ฐ์.
push_back("Alice") ์์ ์ผ์ด๋๋ ์ผ:
1
2
3
"Alice" (const char*)
โ string("Alice") ์์ ๊ฐ์ฒด ์์ฑ โ ์์์ ๋ณํ ๋ฐ์
โ ๊ทธ ์์ ๊ฐ์ฒด๋ฅผ ์ฌ๋กฏ์ผ๋ก ์ด๋
์ค๊ฐ์ ์์ ๊ฐ์ฒด 1๊ฐ๊ฐ ์๊ธด๋ค. emplace_back("Alice") ๋ string("Alice") ๋ฅผ ์ฌ๋กฏ์์ ์ง์ ์์ฑํ๋ฏ๋ก ์์ ๊ฐ์ฒด ์์.
์ T&& ๊ฐ ์๋๋ผ Args&&โฆ ์ธ๊ฐ
T&& ๋ ๋จ์ผ ์ธ์ 1๊ฐ๋ง ๋ฐ์ ์ ์์.
1
2
v.push_back(T&&); // ์ธ์ 1๊ฐ โ BigObject ๋ฏธ๋ฆฌ ๋ง๋ค์ด์ผ ํจ
v.emplace_back(1, 2); // ์ธ์ ์ฌ๋ฌ ๊ฐ โ Point(1, 2) ์ฌ๋กฏ์์ ์ง์ ์์ฑ
์ฌ๋ฌ ์ธ์๋ฅผ ์์ฑ์์ ๊ทธ๋๋ก ์ ๋ฌํด ์์ ๊ฐ์ฒด ์์ด in-place ์์ฑํ๋ ๊ฒ emplace ์ ๋ณธ์ง์ด๋ฏ๋ก Args&&... ๊ฐ ํ์ํจ.
Q4. v.push_back(1, 2.0, โxโ, โฆ) ์ ์ฐ๋ฉด?
์ปดํ์ผ ์๋ฌ. push_back ์ ์ธ์ 1๊ฐ๋ง ๋ฐ์ผ๋ฏ๋ก โtoo many argumentsโ.
1
2
3
4
5
6
7
8
// โ ์ปดํ์ผ ์๋ฌ
v.push_back(1, 2.0, "x", std::vector<int>{1,2,3});
// โ
๊ฐ์ฒด๋ฅผ ๋จผ์ ๋ง๋ค์ด์ผ ํจ
v.push_back(BigObject(1, 2.0, "x", {1,2,3})); // ์์ ๊ฐ์ฒด ์์ฑ ํ ์ด๋
// โ
์ค๊ดํธ ์ด๊ธฐํ (explicit ์๋ ์์ฑ์์ธ ๊ฒฝ์ฐ)
v.push_back({1, 2.0, "x", {1,2,3}});
์ค๊ดํธ ์ด๊ธฐํ๋ explicit ์์ฑ์๊ฐ ์์ ๋๋ง ๋์. explicit ๋ถ์ด์์ผ๋ฉด ์ค๊ดํธ๋ ์๋ฌ.
Q5. emplace_back ์ผ๋ก unique_ptr ์์ฑ์๋ฅผ ํธ์ถํ๋ฉด?
1
2
3
4
5
6
7
std::vector<std::unique_ptr<Resource>> v;
// โ ์ํ: raw new ์ emplace_back ์ฌ์ด์ ์์ธ ๋ฐ์ ์ ๋ฉ๋ชจ๋ฆฌ ๋์
v.emplace_back(new Resource("data"));
// โ
์์
v.emplace_back(std::make_unique<Resource>("data"));
unique_ptr(T* ptr) ์์ฑ์๋ explicit ์ด๋ผ์ push_back(new Resource(...)) ๋ ์ปดํ์ผ ์๋ฌ. emplace_back ์ explicit ์ ์ฐํํด ์ง์ ํธ์ถ ๊ฐ๋ฅ โ ยง4-1 ์ โํจ์ โ๊ณผ ๊ฐ์ ๋งฅ๋ฝ.
์ํํ ์ด์ : new Resource("data") ๊ฐ ์ฑ๊ณตํ๋๋ฐ emplace_back ๋ด๋ถ ์ฌํ ๋น์์ ์์ธ ๋ฐ์ ์, raw ํฌ์ธํฐ๋ฅผ ๋ณด๊ดํ๋ ๊ณณ์ด ์์ด ๋์๋จ. make_unique ๋ ํฌ์ธํฐ๋ฅผ ์ฆ์ unique_ptr ์ ๋ฌถ์ผ๋ฏ๋ก ์์ธ ์์ .
Q6. vector capacity ์ฌํ ๋น์ ์๋? ์๋?
์์ ์๋. size > capacity ๊ฐ ๋๋ ์๊ฐ ๋ด๋ถ์์ ์๋ ๋ฐ๋.
| ย | vector ์ฌํ ๋น | unordered_map rehash |
|---|---|---|
| ํธ๋ฆฌ๊ฑฐ | ์๋ (size > capacity) | ์๋ + ์๋ (u.rehash(n)) |
| ์๋ ํธ๋ฆฌ๊ฑฐ | ์์ | ์์ |
| ํํผ ๋ฐฉ๋ฒ | reserve(n) | reserve(n) |
reserve(n) ์ผ๋ก ์ฌํ ๋น ์์ ์ ๋ฆ์ถ ์๋ง ์๊ณ , ์ง์ ๋ฐ๋์ํค๋ ๋ฐฉ๋ฒ์ ์์.
Q7. ์ฒด์ธ ๊ธธ์ด โ load_factor ๋ป
1
load_factor = ์์ ์(n) / ๋ฒํท ์(m)
์์๊ฐ ๋ฒํท์ ๊ท ๋ฑํ๊ฒ ๋ถํฌ๋๋ค๊ณ ๊ฐ์ ํ๋ฉด ๋ฒํท ํ๋๋น ํ๊ท ์์ ์ = n / m = load_factor.
1
2
์์ 100๊ฐ, ๋ฒํท 100๊ฐ โ load_factor 1.0 โ ๋ฒํท๋น ํ๊ท 1๊ฐ โ find ์ ํ๊ท 1ํ ๋น๊ต
์์ 200๊ฐ, ๋ฒํท 100๊ฐ โ load_factor 2.0 โ ๋ฒํท๋น ํ๊ท 2๊ฐ โ find ์ ํ๊ท 2ํ ๋น๊ต
์ฒด์ธ ๊ธธ์ด(ํ์ ๋น์ฉ)๊ฐ load_factor ์ ๋น๋กํ๋ค = load_factor ๋ฅผ ๋ฎ๊ฒ ์ ์งํ ์๋ก ํ์์ด ๋น ๋ฅด๋ค.
Q8. ํด์์ถฉ๋์ด๋? ์๋ฉธ์์์ ํด์ ํด์ ๊ฐ ์ผ์ด๋๋๊ฐ?
ํด์์ถฉ๋
hash(key) % bucket_count ๊ฒฐ๊ณผ๊ฐ ์๋ก ๋ค๋ฅธ ๋ ํค์์ ๊ฐ์ ๋ฒํท ์ธ๋ฑ์ค ๊ฐ ๋์ค๋ ๊ฒ. ํด์๊ฐ ์์ฒด๊ฐ ๊ฐ์ ํ์๋ ์๊ณ , ๋๋จธ์ง ์ฐ์ฐ ๊ฒฐ๊ณผ(๋ฒํท ์ธ๋ฑ์ค)๊ฐ ๊ฐ์ผ๋ฉด ์ถฉ๋.
1
2
hash("foo") % 16 = 5
hash("xyz") % 16 = 5 โ ์๋ก ๋ค๋ฅธ ํค, ๊ฐ์ ๋ฒํท ์ธ๋ฑ์ค โ ํด์ ์ถฉ๋
์๋ฉธ์์์ ํด์
- ์ฒด์ด๋:
unordered_map์๋ฉธ ์ ๊ฐ ๋ฒํท์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๋ ธ๋๋ค์ ์ํํ๋ฉฐ ์๋ฉธ์ ํธ์ถ +delete - ํด์ ํจ์ ์์ฒด: ํจ์์ผ ๋ฟ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์์ โ ํด์ ํ ๊ฒ ์์
- ์ค์ ํด์ ๋์: ๋ฒํท ๋ฐฐ์ด + (์ฒด์ด๋์ ๊ฒฝ์ฐ) ๊ฐ ๋ ธ๋
Q9. probing ์ ๋ต์ด๋?
์คํ ์ด๋๋ ์ฑ์์ ์ถฉ๋ ์ ์ด๋ค ์์๋ก ๋ค์ ๋น ์ฌ๋กฏ์ ํ์ํ๋๊ฐ ์ ๋ฐฉ๋ฒ.
1
2
3
4
5
6
7
8
์ ํ ํ์ฌ (Linear Probing): +1, +2, +3 ...
โ ์บ์ ์นํ ์ต๊ฐ (์ธ์ ์ฌ๋กฏ), ํด๋ฌ์คํฐ๋ง ์ฌํจ
์ด์ฐจ ํ์ฌ (Quadratic Probing): +1ยฒ, +2ยฒ, +3ยฒ ...
โ ํด๋ฌ์คํฐ๋ง ์ํ, load_factor 0.5 ์ด๊ณผ ์ ๋ฌดํ๋ฃจํ ์ํ
์ด์ค ํด์ฑ (Double Hashing): +h2(k), +2ยทh2(k) ...
โ ๋ถํฌ ๊ฐ์ฅ ๊ท ๋ฑ, ํด์ ํจ์ 2๋ฒ ๊ณ์ฐ ๋น์ฉ
๋ฐฐ์ด ์์์ ํ์ ์์๋ฅผ ๊ฒฐ์ ํ๋ ๊ท์น = probing ์ ๋ต.
Q10. โnext ํฌ์ธํฐ ๋ถํ์โ โ ํด์์ next ๊ฐ ์์ง ์์๊ฐ?
์ฒด์ด๋ ๊ณผ ์คํ ์ด๋๋ ์ฑ ์ ์๋ก ๋ค๋ฅธ ์๋ฃ๊ตฌ์กฐ. next ํฌ์ธํฐ๋ ์ฒด์ด๋์๋ง ์์.
1
2
3
4
5
6
7
// ์ฒด์ด๋ (Separate Chaining) โ next ์์
struct Node { Key k; Value v; Node* next; };
// buckets[i] ๋ Node* (์ฐ๊ฒฐ ๋ฆฌ์คํธ ํค๋)
// ์คํ ์ด๋๋ ์ฑ (Open Addressing) โ next ์์
struct Slot { Key k; Value v; bool occupied; };
// buckets[i] ๋ Slot (๊ฐ ์ง์ ์ ์ฅ)
โ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋ ์์ โ next ํฌ์ธํฐ ๋ถํ์โ ๋ ์คํ ์ด๋๋ ์ฑ ์ ์ฅ์ ์ ์ค๋ช ํ ๊ฒ. ์ฒด์ด๋์ next ํฌ์ธํฐ์๋ ๋ค๋ฅธ ์๊ธฐ.
Q11. reserve ๋ก iterator ๋ฌดํจํ ํํผํ๋ ๋ฐฉ๋ฒ
1
2
3
4
5
6
7
8
9
10
11
12
std::vector<int> v;
v.reserve(1000); // capacity = 1000, size = 0
int* ptr = v.data(); // ํฌ์ธํฐ ์ ์ฅ
for (int i = 0; i < 1000; i++) {
v.push_back(i); // size <= capacity โ ์ฌํ ๋น ์์ โ ptr ์ฌ์ ํ ์ ํจ
}
// 1001๋ฒ์งธ push_back โ size > capacity โ ์ฌํ ๋น ๋ฐ์ โ ptr ๋๊ธ๋ง!
v.push_back(1001);
// ์ด์ ptr ์ ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ๋ฆฌํด (UB)
ํต์ฌ: reserve ํ ๋ฒ์ ๋ด์์๋ง ์์ . ๊ทธ ๋ฒ์๋ฅผ ๋๋ ์๊ฐ ์ฌํ ๋น์ด ๋ฐ์ํ๊ณ ๋ชจ๋ iteratorยทํฌ์ธํฐยท์ฐธ์กฐ๊ฐ ๋ฌดํจํ๋จ.
์ค์ ํจํด:
1
2
3
4
5
std::vector<Obj> v;
v.reserve(expected_size); // ์์ ํฌ๊ธฐ ๋ฏธ๋ฆฌ ํ๋ณด
for (auto& item : source)
v.emplace_back(item); // ์ฌํ ๋น 0ํ โ iterator ์์
Q12. vector ์์ emplace_back ์ ์ฐ๋ฉด ํด์ ์ถฉ๋์ด ์ ์ผ์ด๋๋๊ฐ?
์ ํ ๋ฌด๊ดํ ๊ฐ๋ ์ด๋ค.
| ย | std::vector | std::unordered_map |
|---|---|---|
| ๋ด๋ถ ๊ตฌ์กฐ | ์ฐ์ ๋ฐฐ์ด | ๋ฒํท ๋ฐฐ์ด + ํด์ ํจ์ |
| ํค ๊ฐ๋ | ์์ (์ธ๋ฑ์ค ์ ๊ทผ) | ์์ (key โ hash โ bucket) |
| ์ถฉ๋ ๊ฐ๋ | ์์ | ์์ |
| ์์ ์ถ๊ฐ | push_back / emplace_back | insert / emplace |
1
2
3
4
5
6
7
// vector โ ํด์ ์์, ์ถฉ๋ ์์
std::vector<int> v;
v.emplace_back(42); // ์ฌ๋กฏ[size] ์ 42 ์ง์ ์์ฑ. ๋.
// unordered_map โ ํด์ ์์, ์ถฉ๋ ๊ฐ๋ฅ
std::unordered_map<std::string, int> m;
m.emplace("foo", 1); // hash("foo") % N โ ๋ฒํท ์ ํ โ ์ถฉ๋ ์ฌ๋ถ ํ์ธ
unordered_map ์๋ emplace ๊ฐ ์์ง๋ง emplace_back ๊ณผ ๋ค๋ฅธ ํจ์. emplace_back ์ vectorยทdeque ๊ฐ์ ์ํ์ค ์ปจํ
์ด๋ ์๋ง ์์.
๋ ๊ฐ๋ ์ด ๊ฐ์ ํ์ผ์ ๋ฌถ์ธ ์ด์
์ถฉ๋์ด ์ฐ๊ด๋ ๊ฒ ์๋๋ผ ์ฌํ์ฅ ํจํด์ด ๊ฐ์์ ๋น๊ตํ ๊ฒ:
1
2
3
4
5
6
vector ์ฌํ ๋น โ unordered_map rehash
size > capacity load_factor > max_load_factor
์ ๋ฐฐ์ด + ์ ์ฒด ์ด๋ ์ ๋ฒํท + ์ ์ฒด ์ฌ๋ฐฐ์น
O(n) ๋น์ฉ O(n) ๋น์ฉ
iterator ์ ๋ถ ๋ฌดํจํ iterator ์ ๋ถ ๋ฌดํจํ
reserve(n) ๋ก ํํผ reserve(n) ๋ก ํํผ