Skip to content
Laviyx Learn

Try

C++ course menu

C++ std::vector: Dynamic Arrays Explained with Examples

Learn std::vector, C++'s most used container: creating vectors, push_back and emplace_back, accessing with [] and at(), size and capacity, inserting and erasing, sorting, removing elements safely, vectors of strings and objects, and passing vectors to functions.

Beginner12 min readUpdated
On this page

std::vector (from <vector>) is a resizable array. It stores elements next to each other in memory like an array, so it is fast, but it grows automatically as you add elements. It is the container you will use most in C++; when in doubt, use a vector.

Creating a vector#

C++
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<int> empty;                    // no elements
    std::vector<int> scores = {72, 88, 95};    // three elements
    std::vector<int> zeros(5);                 // five zeros
    std::vector<std::string> names(3, "n/a");  // three copies of "n/a"

    std::cout << empty.size() << ' ' << scores.size() << ' '
              << zeros.size() << ' ' << names[2] << '\n';
}
Output
0 3 5 n/a

The type in angle brackets is the element type: std::vector<double>, std::vector<std::string>, and so on.

Adding and removing at the end#

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> stack;
    stack.push_back(10);
    stack.push_back(20);
    stack.push_back(30);
    std::cout << "Last: " << stack.back() << ", size: " << stack.size() << '\n';

    stack.pop_back();   // removes the last element
    std::cout << "Last: " << stack.back() << ", size: " << stack.size() << '\n';
}
Output
Last: 30, size: 3
Last: 20, size: 2

Adding or removing at the end is fast. emplace_back(args...) builds the new element in place from constructor arguments, which is handy for objects.

Accessing elements#

AccessMeaning
v[i]Element at index i, no bounds check
v.at(i)Element at index i, throws std::out_of_range if invalid
v.front() / v.back()First / last element (vector must not be empty)
v.empty()true if there are no elements

Calling front(), back() or pop_back() on an empty vector is undefined behaviour, so check empty() first when the vector might be empty.

Looping#

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<double> prices = {120.0, 45.5, 300.0};

    for (double& p : prices) {
        p *= 1.18;          // add 18% tax in place
    }
    for (std::size_t i = 0; i < prices.size(); ++i) {
        std::cout << i << ": " << prices[i] << '\n';
    }
}
Output
0: 141.6
1: 53.69
2: 354

Size and capacity#

A vector reserves some spare room so it does not need to reallocate on every push_back. size() is the number of elements; capacity() is how many fit before it must grow:

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;
    v.reserve(100);   // allocate room for 100 elements up front
    for (int i = 0; i < 10; ++i) v.push_back(i);

    std::cout << v.size() << ' ' << v.capacity() << '\n';
}
Output
10 100

When a vector grows past its capacity, it allocates a bigger block and moves every element. If you know roughly how many elements you will add, reserve avoids those repeated moves. Growth also means that references, pointers and iterators to elements become invalid after a push_back that reallocates.

Inserting and erasing in the middle#

insert and erase take an iterator (a position), usually written as v.begin() + index:

C++
#include <iostream>
#include <vector>

void print(const std::vector<int>& v) {
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';
}

int main() {
    std::vector<int> v = {10, 20, 30, 40};

    v.insert(v.begin() + 1, 15);   // insert before index 1
    print(v);

    v.erase(v.begin() + 3);        // remove index 3
    print(v);

    v.clear();                     // remove everything
    std::cout << "Empty: " << std::boolalpha << v.empty() << '\n';
}
Output
10 15 20 30 40
10 15 20 40
Empty: true

Inserting or erasing near the front moves every element after it, so it is slow for large vectors. You will learn more about iterators in Iterators.

Removing all matching elements#

To remove every element that matches a condition, C++20 provides std::erase and std::erase_if:

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {5, -2, 8, -7, 3, 0, -1};

    std::erase_if(nums, [](int n) { return n < 0; });

    for (int n : nums) std::cout << n << ' ';
    std::cout << '\n';
}
Output
5 8 3 0

[](int n) { return n < 0; } is a lambda, a small unnamed function; see Lambdas. Before C++20, the same job needed the "erase-remove idiom" with std::remove_if.

Sorting and searching#

The algorithms in <algorithm> work on vectors:

C++
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> names = {"Ravi", "Asha", "Meera", "John"};

    std::sort(names.begin(), names.end());
    for (const auto& n : names) std::cout << n << ' ';
    std::cout << '\n';

    auto it = std::find(names.begin(), names.end(), "Meera");
    if (it != names.end()) {
        std::cout << "Meera is at index " << (it - names.begin()) << '\n';
    }
}
Output
Asha John Meera Ravi
Meera is at index 2

Algorithms covers many more.

Passing vectors to functions#

Pass by const reference to read, by reference to modify, and return by value to produce a new vector:

C++
#include <iostream>
#include <vector>

std::vector<int> evens(const std::vector<int>& values) {
    std::vector<int> result;
    for (int v : values) {
        if (v % 2 == 0) result.push_back(v);
    }
    return result;
}

int main() {
    for (int e : evens({3, 8, 12, 5, 6})) std::cout << e << ' ';
    std::cout << '\n';
}
Output
8 12 6

Returning a vector by value is cheap in modern C++, because it is moved (or built directly in place), not copied.

Common mistakes#

Practice#

Practice exercise

Read numbers from std::vector<int> data = {4, 9, 4, 1, 9, 9, 7}; into a new vector that keeps only the first occurrence of each value, in the original order. Print it.

Show solution
C++
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> data = {4, 9, 4, 1, 9, 9, 7};
    std::vector<int> unique;

    for (int x : data) {
        if (std::find(unique.begin(), unique.end(), x) == unique.end()) {
            unique.push_back(x);
        }
    }
    for (int x : unique) std::cout << x << ' ';
    std::cout << '\n';
}
Output
4 9 1 7

For large inputs, track seen values in a std::unordered_set instead, which makes each check fast; see unordered_map and unordered_set.

Practice exercise

Write std::vector<int> running_total(const std::vector<int>& v) that returns the running totals, so {3, 1, 4, 1, 5} gives {3, 4, 8, 9, 14}.

Show solution
C++
#include <iostream>
#include <vector>

std::vector<int> running_total(const std::vector<int>& v) {
    std::vector<int> result;
    result.reserve(v.size());
    int sum = 0;
    for (int x : v) {
        sum += x;
        result.push_back(sum);
    }
    return result;
}

int main() {
    for (int x : running_total({3, 1, 4, 1, 5})) std::cout << x << ' ';
    std::cout << '\n';
}
Output
3 4 8 9 14

Key takeaway#

Finished this lesson?