Project 11.09 Section 11 ⚡ Embedded Relevance: Critical std::vector push_back Capacity Growth reserve() Reallocation Latency

11.09 Capacity Doubling, Reallocation Overhead, and reserve() Optimization

Executive Summary: Dissecting std::vector mechanics. We explore geometric capacity doubling, sudden heap reallocations during push_back(), pointer invalidation, and how reserve() guarantees deterministic performance.

💻 1. Annotated Source Code

#include <iostream>
#include <deque>
using namespace std;

void printDeque(const deque<int>& deck);

int main() {
	deque<int> myDeck;

	myDeck.push_back(1);
	myDeck.push_back(5);
	myDeck.push_back(10);

	cout << "First print:" << endl;
	printDeque(myDeck);

	myDeck.push_front(20);
	myDeck.push_front(30);

	cout << "Next print:" << endl;
	printDeque(myDeck);

	return 0;
}

void printDeque(const deque<int>& deck) {
	for (int num : deck) {
		cout << num << endl;
	}
	cout << endl;
}

📐 2. Architecture & UML Class Model

📐 STL Sequential Containers (vector, deque, list) Architecture
+ Public - Private # Protected
<<template class>> std::vector<T> Contiguous Array
-_M_start : T*
-_M_finish : T*
+push_back(val: const T&) : void
+operator[](idx: size_t) : T&
<<template class>> std::deque<T> Chunked Map
-_M_map : T**
+push_front(val: const T&) : void
+push_back(val: const T&) : void
<<template class>> std::list<T> Doubly-Linked List
-_M_node : _List_node_base
+insert(pos: iterator, val: const T&) : iterator
+erase(pos: iterator) : iterator

📚 3. Core C++ Concepts Deep-Dive

1. Size vs Capacity and Geometric Growth

A vector maintains size() (active elements) and capacity() (allocated slots). When size() == capacity(), the next push_back() allocates a new block (typically $1.5 imes$ or $2 imes$ size), copies/moves existing elements, and frees the old block.

⚡ 4. Embedded Systems & Hardware Reality

1. The Reallocation Latency Spike in Microcontrollers

A single push_back() can unexpectedly trigger a heavy memory reallocation, copying hundreds of elements and causing non-deterministic execution spikes.

💡 Mandatory Best Practice: Always Use reserve()

If maximum capacity is known in advance, call vec.reserve(MAX_SIZE) during initialization to eliminate all runtime reallocations.

💡 5. Production-Ready Embedded Refactoring

💡 Production-Ready Refactor
#include <vector>
#include <iostream>

void initAdcTelemetry() {
    std::vector<uint16_t> adcSamples;
    adcSamples.reserve(256); // Pre-allocate: ZERO reallocations during sampling!

    for (int i = 0; i < 256; ++i) {
        adcSamples.push_back(i * 4); // Deterministic O(1) insertion
    }
}

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. What happens when push_back() is called on a std::vector whose size() is equal to its capacity()?
A The element is silently discarded.
B A new larger heap memory block is allocated, all existing elements are copied or moved, the old block is freed, and existing iterators/pointers are invalidated.
C A compile-time error occurs.
D The vector size becomes 0.
Detailed Explanation: When capacity is exceeded, the vector must allocate a new larger memory block, move all elements over, and release the old memory, invalidating all iterators and pointers to its elements.