- Vectors
Published:
template <typename T>- Change
inttoT - Make functions template functions parameterized by
T```cpp templatestd::ostream& operator<<(std::ostream& os, const vec & ia);
Vec
```cpp
void push_back(int x) {
if (sz == cap) {
int* a2 = new int[cap * 2];
cap *= 2;
std::copy(a, a+sz, a2); // uses copy assignment
delete[] a;
a = a2;
}
a[sz++] = x;
}
int main() {
Vec<MyString> v; // (1)
v.push_back("abc"); // (2)
v.push_back("def"); // (3)
MyString s{"xyz"}; // (4)
v.push_back(s); // (5)
}
- Constructs
von the stack{size_t size, size_t cap, MyString a[1]}sz = 0; cap = 1;ais an allocated 1-element array- Calls
MyString()- Makes a default
MyStringobject on the heap, with
- Makes a default
- Calls
const T&requires a promoted temporary object- Temp (rvalue)
MyString("abc")object constructed on stacklen = 3data = "abc\0"
- Temp comes into
push_back()asconst T& x(reference)- Must have
const, else won’t accept rvalue
- Must have
a[sz++] = x- Copy assignment
xdestroyed afterpush_back()return, and temp
- Temp (rvalue)
push_back("def")- Same temp creation
- Allocation needed! (two levels)
new MyString[2]allocated on heap- Both members are default constructed
MyString()
- Both members are default constructed
std::copy()- Copy assignment
deleteold data- Data pointer updated
- Copy assignment
delete[] adelete[]all string allocations
x(and its members) gets copy assigned to itsa[1], and destroyed
MyString s{"xyz"}screated on stack"xyz\0"on heap
v.push_back(s)spassed by reference- Need to reallocate again
- Every
MyStringgets default constructed - Copy assign the
MyStrings over - The vector entry
v.a[3].datahas a different copy withs.data - Last slot left "empty", but aMyString` object
- Every
STL containers hold its own copy of the data (by value)
[!info] This is NOT how
vectorworks. Default construction is wastefulstd::vectorwill not initialize the memory (no default constructornew, justmalloc())
- But how would
a[sz++] = xwork?
- This is an assignment. The LHS object must be constructed
- Solution: Placement
newPlacement
new
char s_buf[sizeof(MyString)];
MyString* sp = new (s_buf) MyString{"hello"};
Casting decouples memory allocation and constructor call!
- No memory allocation
- Only calls constructor Can’t directly invoke
delete, need to manually invoke the destructorsp->~MyString();
Strong Exception Guarantee
For push_back(Int), exception can only happen in new. std::copy() of integers is trivial
- Need to guarantee that
IntArraywon’t be changed
For push_back(MyString),
- If
newthrows, nothing created - If
copy()throws,operator=(MyString)usesnew, may throw
- Assignment
a[sz++] = xmay throw
void push_back(const T& x) {
if (sz == cap) {
T* a2 = new T[cap * 2]; // no issue
try {
std::copy(a, a+sz, a2);
} catch (...) { // catch any exception
delete[] a2; // cleanup before throw
throw;
}
cap *= 2;
delete[] a;
a = a2;
}
a[sz] = x; // if throw here, all did was increasing cap
sz++; // separate sz++
}
[!warning] Still issues! On
a[sz] = x,operator=(MyString)may leave the default constructor in a bad state - Assume if copy assignment fails, it still leaves a valid object there
[!info] Might be more efficient if the elements are moved, rather than copy
std::vectortries to move elements during reallocation, but
- Move may not easy to roll back on exception
std::vector uses the criteria:
- Moved if move constructor marked
noexcept - Copy else
- If copy constructor deleted, the fall back to move (forgo strong exception guarantee)
