An empty vector
std::vector<int> v; makes an empty vector, the standard library's go-to container. The object v lives on the stack and holds three things: where its elements are (data), how many it has (size) and how many fit (capacity).
Filling it
v.reserve(2) asks the heap for room for two ints, side by side. data now holds that block's address, 0x5a10, and capacity becomes 2. size stays 0: the room is reserved but holds no elements yet.
v.push_back(10) puts 10 in the first free slot and adds 1 to size. There was spare capacity, so no new memory is needed. This is the common, fast case.
v.push_back(20) fills the second slot. Now size equals capacity: 2 of 2. The elements sit next to each other in memory, so v[i] is one quick address sum and loops run fast.
Growing
v.push_back(30) finds no room. A vector cannot stretch its block in place, so it asks the heap for a new, bigger one. libstdc++ (GCC) and libc++ (LLVM) double the capacity: room for 4 ints at 0x5a30.
Next, libstdc++ and libc++ build the new element first: 30 goes into slot [2] of the new block. The old block still holds 10 and 20, and data still points at it. If making 30 failed, v would be untouched.
Then 10 and 20 are moved across, in order (copied if moving could throw). The old block goes back to the heap, data points at 0x5a30, size is 3 and capacity 4. All inside one push_back call.
Suppose you took int* p = &v[0]; before line 8. p still holds 0x5a10, but that block was freed. Using *p is undefined behaviour. Growing invalidates every pointer, reference and iterator to the elements.
Using and freeing
for (int x : v) visits each element in order, from v.begin() to v.end(). x takes 10, then 20, then 30, and line 10 prints each one with a space. The output reads 10 20 30.
At the closing brace v goes out of scope. Its destructor destroys the elements and frees the heap block, with no delete written anywhere. Every standard container, from std::list to std::map, owns its elements this way.
What happens
;; step 1/10 · vector 4 std::vector<int> v; v on the stack: data = nullptr size = 0 capacity = 0 no elements yet, so no heap memory yet sizeof(v) = 24 bytes with libstdc++ or libc++ on 64-bit, however many elements the elements will live on the heap, side by side
The C++ standard does not fix how much a vector grows, only that push_back takes amortised constant time. GCC's libstdc++ and LLVM's libc++ double the capacity (2 becomes 4); Microsoft's STL grows by half (2 becomes 3). reserve(2) gives a capacity of at least 2; all three give exactly 2. The addresses 0x5a10 and 0x5a30 are made up and short so they fit. Real ones are longer, change from run to run, and the new block need not sit right after the old one.