DEV Community

Saiful Islam
Saiful Islam

Posted on

Vectors in C++: The Smart Dynamic Array

When coding in C++, one of the most common data structures you’ll encounter is the vector.

It behaves like an array but with runtime flexibility — it can grow or shrink dynamically, making it one of the most powerful tools in the Standard Template Library (STL).

In this guide we’ll cover:

  • Vectors and STL basics
  • Initialization methods
  • Common functions and operations
  • Static vs dynamic allocation
  • Array vs vector differences
  • How vectors manage memory
  • Iterators
  • 2D vectors
  • Performance considerations
  • Comparison with other containers
  • Automatic memory cleanup

  1. STL and Vectors

The Standard Template Library (STL) provides ready-made containers and algorithms. Some commonly used containers:

  • vector → Dynamic array
  • queue → FIFO
  • stack → LIFO
  • set → Unique, ordered elements

👉 Compilation note: always use at least C++11:

g++ -std=c++11 code.cpp -o runfile && ./runfile
Enter fullscreen mode Exit fullscreen mode

If you forget -std=c++11, you may run into compile errors or unexpected runtime issues.


  1. Vector Initialization

Different ways to initialize std::vector:

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

int main() {
    // Direct initialization (C++11)
    vector<int> v1 = {1, 2, 3};

    // Fill constructor (size, initial_value)
    vector<int> v2(3, 0); // [0, 0, 0]

    // Empty then push_back
    vector<int> v3;
    v3.push_back(10);
    v3.push_back(20);

    // Printing using range-for loop
    for (int x : v1) cout << x << " ";
    cout << endl;
}
Enter fullscreen mode Exit fullscreen mode

Output:

1 2 3
Enter fullscreen mode Exit fullscreen mode

  1. Vector Functions & Operations

Common vector operations:

  • Insertion / Removal

    • push_back(x) → add at end
    • pop_back() → remove last element
  • Access

    • v[i] → element by index (no bounds check)
    • v.at(i) → bounds-checked access (throws out_of_range)
    • front() / back()
  • Information

    • size() → number of elements
    • capacity() → allocated memory size
    • empty() → check if empty

Example:

vector<int> v = {10, 20, 30};
v.push_back(40);  // [10,20,30,40]
v.pop_back();     // [10,20,30]

cout << v.front() << endl;    // 10
cout << v.back() << endl;     // 30
cout << v.at(1) << endl;      // 20
cout << v.size() << endl;     // 3
cout << v.capacity() << endl; // capacity >= 3
Enter fullscreen mode Exit fullscreen mode

  1. Static vs Dynamic Memory Allocation

Static arrays

int arr[5]; // size fixed at compile-time
Enter fullscreen mode Exit fullscreen mode
  • Usually stored on the stack
  • Size rigid, cannot grow

Dynamic arrays (manual)

int* arr = new int[5];
delete[] arr;
Enter fullscreen mode Exit fullscreen mode
  • Stored on the heap
  • Requires manual memory management

Vectors use dynamic memory under the hood but manage it automatically, making them safer and more flexible than raw dynamic arrays.


  1. Array vs Vector — Key Differences
Feature Array (Static) Vector (Dynamic)
Size Fixed at compile-time Can grow/shrink at runtime
Memory location Stack (usually) Heap (managed internally)
Memory management Manual (if dynamic) Automatic
Functions available None (raw access only) Rich API: push_back, at, size...
Initialization Limited forms Flexible constructors
Safety No bounds checking at() provides bounds-checked access

  1. How Vectors Work in Memory

vector maintains two core concepts:

  • size → number of stored elements
  • capacity → allocated storage space (may be >= size)

When capacity is exceeded, implementations typically grow the capacity (commonly by doubling), which amortizes insertion costs.

Example:

vector<int> v;
for (int i = 0; i < 10; i++) {
    v.push_back(i);
    cout << "Size: " << v.size()
         << " Capacity: " << v.capacity() << endl;
}
Enter fullscreen mode Exit fullscreen mode

Typical output (implementation-dependent):

Size: 1 Capacity: 1
Size: 2 Capacity: 2
Size: 3 Capacity: 4
Size: 5 Capacity: 8
Size: 9 Capacity: 16
Enter fullscreen mode Exit fullscreen mode

Use reserve(n) to pre-allocate memory if you know the expected size — this avoids repeated reallocations.


  1. Iterators

Iterators behave like pointers and are used to traverse containers:

vector<int> v = {10,20,30,40};

// Forward
for (auto it = v.begin(); it != v.end(); ++it) cout << *it << " ";

// Reverse
for (auto it = v.rbegin(); it != v.rend(); ++it) cout << *it << " ";
Enter fullscreen mode Exit fullscreen mode

Output:

10 20 30 40
40 30 20 10
Enter fullscreen mode Exit fullscreen mode

Iterators are the common way to use STL algorithms like std::sort, std::find, etc.


  1. 2D Vectors

Nested vectors are useful for matrices, graphs, and dynamic 2D storage:

vector<vector<int>> matrix(3, vector<int>(3, 0));
matrix[0][1] = 5;
matrix[2][2] = 7;

for (auto& row : matrix) {
    for (auto val : row) cout << val << " ";
    cout << endl;
}
Enter fullscreen mode Exit fullscreen mode

Output:

0 5 0
0 0 0
0 0 7
Enter fullscreen mode Exit fullscreen mode

  1. Performance Tips
  • size() is O(1). Use it freely.
  • capacity() shows reserved memory; use reserve(n) when you know the expected size.
  • Avoid excessive copying — prefer emplace_back() to construct in-place.
  • Random access is O(1); inserting/removing at the middle is O(n).
  • For frequent front insertions, consider deque; for frequent middle insert/delete, consider list (but lists have poor cache locality).

  1. Vector vs Other Containers
  • Vector → best for random access, fast end insertions.
  • Deque → fast insertions at both ends.
  • List → fast middle insertions/deletions (no random access).
  • Set → unique, ordered elements (logarithmic operations).

👉 Default choice: vector unless you have a specific reason to choose another container.


  1. Automatic Memory Cleanup

Vectors free their managed memory automatically when they go out of scope:

void func() {
    vector<int> v(1000, 1);
} // memory is released when the function returns
Enter fullscreen mode Exit fullscreen mode

✅ Conclusion

std::vector is array-like but flexible, safe (with at()), and efficient for most general-purpose needs. Prefer vectors over raw arrays unless you need a fixed-size, stack-allocated buffer or have very specific performance constraints.


Top comments (0)