-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvector.cpp
More file actions
132 lines (104 loc) · 4.75 KB
/
vector.cpp
File metadata and controls
132 lines (104 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-value"
#pragma clang diagnostic ignored "-Wunused-result"
#define CATCH_CONFIG_MAIN // Tells Catch2 to provide a main()
#include "../catch/catch_amalgamated.hpp"
#include <algorithm>
#include "utils.h"
namespace {
using namespace std;
// vector
// - dynamic array, contiguous memory, O(1) random access
//
// Key notes:
// - contiguous memory — cache friendly, best default container
// - push_back may reallocate — invalidates all iterators
// - reserve() before bulk inserts — avoid repeated reallocation ✓
// - emplace_back() over push_back() — constructs in-place, no temp
// - operator[] no bounds check — use at() during development
// - prefer std::erase / std::erase_if over erase-remove idiom (C++20)
TEST_CASE("vec-0") {
//// create
std::vector<int> v4; // empty
std::vector<int> v3 (5); // 5 zeros - !! cannot use {} init
std::vector<int> v2 (3, 42); // {42,42,42} - !! cannot use {}
std::vector<int> v1 {1, 2, 3, 4, 5}; // initializer list
std::vector<int> vec { v1.begin(), v1.end() }; // from range
//// 2D vector
vector<vector<int>> grid(3, std::vector<int>(4, 0)); // 3x4 zeros
REQUIRE(v3 == std::vector {0,0,0,0,0});
REQUIRE(v2 == std::vector {42,42,42});
//// access
vec[0]; // !! no bounds check — UB if out of range
// vec.at(0); // bounds checked — throws ✓
vec.front(); // first element
vec.back(); // last element
int* dataPtr = vec.data(); // raw pointer to buffer
REQUIRE(*dataPtr == 1);
//// insert
vec.push_back(42); // append — copy
// vec.emplace_back("bob", 42) // construct in-place — prefer ✓
// for ctors, use emplace_back() instead of push_back()
// vec.push_back(Foo {"bob", 42});
int val = 42;
vec.push_back(std::move(val)); // append — move ✓
vec.insert(vec.begin(), 42); // insert at position — O(n)
vec.insert(vec.begin() + 2, 42); // insert at index 2
//// remove
vec.pop_back(); // remove last — O(1)
vec.erase(vec.begin()); // remove first — O(n)
// !! erase vec.end() = UB
vec.erase(vec.begin() + 2); // remove at index — O(n)
vec.erase(vec.begin(), vec.begin() + 3); // remove range
std::erase(vec, 42); // remove all 42s — C++20 ✓
// cpp20
std::erase_if(vec, [](int e) { return e < 0; } );
// cpp11 - erase-remove idiom
vec.erase(
std::remove_if( vec.begin(), vec.end(), [](int e ) { return e < 0; } ),
vec.end()
);
// pre-cpp11
for (auto it = vec.begin(); it != vec.end();) {
if (*it < 0)
it = vec.erase(it); // !! iter invalidation risk
// the risk: erase invalidates all iterators after the erased
// position, but since erase returns the next valid iterator
// and we assign it back to it, this specific code is actually
// safe.
else
++it;
}
//// iterate
for (const auto& x : vec) {} // range-for - cpp11
for (size_t i = 0; i < vec.size(); i++) {} // index - cpp98
for (auto it = vec.begin(); it != vec.end(); ++it) {} // - cpp11
//// size / capacity
vec.size(); // number of elements
vec.capacity(); // allocated slots
vec.resize(10); // grow/shrink — new elements zero-init
vec.resize(10, 99); // grow with value
vec.reserve(1000); // perf: pre-allocate — avoid rehashing ✓
for (int i = 0; i < 1000; ++i)
vec.push_back(i); // no reallocation — buffer pre-allocated
vec.shrink_to_fit(); // release excess memory
// reduces capacity() to match size() — useful after many erases to
// free unused reserved memory; it's a non-binding hint (compiler may
// ignore it).
//// clear
vec.clear(); // size = 0, capacity unchanged
vec = {}; // size = 0, capacity unchanged
//// algorithms
std::ranges::find(vec, 42); // C++20 ✓
std::ranges::find_if(vec, [](int x){ return x > 3; });
// cpp11, due to lambda is cpp11
std::find_if(vec.begin(), vec.end(), [](int x){ return x > 3; });
std::find(vec.begin(), vec.end(), 42); // cpp98
std::ranges::sort(vec); // C++20 ✓
std::sort(vec.begin(), vec.end()); // cpp98
//// copy / move
std::vector<int> v7 { vec }; // copy ctor
std::vector<int> v5 = vec; // copt ctor
auto v6 = std::move(vec); // move ctor — v now empty ✓
}
}