SrcForge

Lesson 36

Vector

Covers creating and initializing vectors, common vector operations, accessing elements, iterating through vectors, and passing vectors to functions.

Lesson content

What is STL Vector in C++?

A vector is a sequence container in the Standard Template Library (STL) that stores elements in contiguous memory, just like an array. Unlike a traditional array, however, a vector can automatically resize itself as elements are added or removed.

Think of a vector as a stretchable shopping basket. A normal array is like a box with a fixed number of compartments, you can't easily make it larger. A vector, on the other hand, expands when you need more space and can shrink when items are removed.

Why does STL Vector exist?

Traditional arrays have several limitations: their size must be known at compile time (or managed manually), resizing requires creating a new array and copying elements, and managing memory manually can lead to errors.

  • Automatically managing memory
  • Growing dynamically
  • Providing many useful built-in functions
  • Working seamlessly with STL algorithms

Real-world use cases

  • Storing lists of students or employees
  • Managing game objects in game development
  • Holding sensor readings in IoT systems
  • Processing images and multimedia data
  • Implementing graphs and other data structures
  • Building dynamic collections in web and desktop applications

Prerequisites

  • Variables and data types
  • Arrays
  • Loops (for, while, range-based for)
  • Functions
  • Basic classes and objects
  • Templates (recommended but not mandatory)

Including the vector header

#include <vector>

Creating a vector

vector<data_type> vector_name;

// Example:
vector<int> numbers;

// This creates an empty vector of integers.

Initializing a vector

  • vector<int> v; – empty vector
  • vector<int> v(5); – five elements initialized to 0
  • vector<int> v(5, 10); – five elements, each initialized to 10
  • vector<int> v = {1,2,3}; – initialize using a list

Common vector operations

  • push_back() – add an element to the end
  • pop_back() – remove the last element
  • size() – number of elements
  • empty() – check if the vector is empty
  • clear() – remove all elements
  • front() – first element
  • back() – last element
  • at() – safe indexed access
  • operator[] – fast indexed access (no bounds checking)
  • insert() – insert an element
  • erase() – remove one or more elements
  • resize() – change the size of the vector
  • reserve() – reserve memory capacity

How vectors grow

When a vector runs out of space, it allocates a larger block of memory, copies or moves existing elements, and releases the old memory.

This process is automatic, which makes vectors easy to use but can occasionally impact performance if frequent reallocations occur.

Accessing elements

There are two common ways to access elements.

// Using []
numbers[2];
// Fast but does not check bounds.

// Using at()
numbers.at(2);
// Safer because it checks whether the index is valid.

Iterating through a vector

You can iterate using an index-based for loop, a range-based for loop, or iterators.

for (int value : numbers)
{
    cout << value << " ";
}

Example 1: Creating and displaying a vector

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

int main()
{
    vector<int> numbers;

    numbers.push_back(10);
    numbers.push_back(20);
    numbers.push_back(30);

    for (int num : numbers)
    {
        cout << num << " ";
    }

    return 0;
}

// Output: 10 20 30

Example 2: Accessing and modifying elements

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

int main()
{
    vector<int> marks = {80, 85, 90};

    cout << marks.front() << endl;
    cout << marks.back() << endl;

    marks.at(1) = 95;

    for (int mark : marks)
    {
        cout << mark << " ";
    }

    return 0;
}

Example 3: Inserting and erasing elements

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

int main()
{
    vector<int> numbers = {1, 2, 4, 5};

    numbers.insert(numbers.begin() + 2, 3);

    numbers.erase(numbers.begin());

    for (int value : numbers)
    {
        cout << value << " ";
    }

    return 0;
}

// Output: 2 3 4 5

Example 4: Using vectors with functions

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

void printVector(const vector<int>& v)
{
    for (int value : v)
    {
        cout << value << " ";
    }

    cout << endl;
}

int main()
{
    vector<int> data = {5, 10, 15, 20};

    printVector(data);

    return 0;
}

Example 5: Student marks management

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

int main()
{
    vector<int> marks;

    marks.push_back(78);
    marks.push_back(91);
    marks.push_back(85);

    int total = 0;

    for (int mark : marks)
    {
        total += mark;
    }

    double average = static_cast<double>(total) / marks.size();

    cout << "Average Marks: " << average;

    return 0;
}

Common mistakes and pitfalls

  • Using numbers[10] without checking size. Fix: use numbers.at(10) or validate the index first
  • Forgetting to include <vector>. Fix: always #include <vector>
  • Passing vectors by value unnecessarily. Fix: pass by const vector<T>& when not modifying the vector
  • Using erase() while iterating incorrectly. Fix: update the iterator returned by erase()
  • Assuming reserve() changes size. Fix: use resize() to change the number of elements

Best practices

  • Prefer vectors over raw arrays for dynamic collections
  • Use const vector<T>& when passing vectors to functions without modification
  • Call reserve() when you know approximately how many elements will be added to reduce reallocations
  • Use emplace_back() instead of push_back() when constructing objects directly in the vector
  • Prefer range-based for loops for readability
  • Use at() when safety is more important than maximum performance
  • Remove unnecessary elements with erase() and clear() to keep the container manageable

When not to use this

  • You frequently insert or remove elements at the beginning or in the middle (consider std::list or std::deque)
  • You require automatic sorting and uniqueness (use std::set)
  • You need fast key-value lookups (use std::map or std::unordered_map)
  • The collection size is fixed and known at compile time (consider std::array)

Summary

  • STL Vector in C++ is a dynamic array that automatically manages memory
  • Vectors store elements in contiguous memory, enabling fast random access
  • Use push_back() to add elements and pop_back() to remove the last element
  • Access elements with [], at(), front(), and back()
  • Common operations include insert(), erase(), resize(), reserve(), clear(), and size()
  • Pass vectors by const reference to avoid unnecessary copying
  • Use reserve() to improve performance when the number of elements is known
  • Choose vectors for most dynamic sequential storage needs, but consider other STL containers for specialized use cases

Frequently asked questions

What is the difference between an array and a vector in C++?

An array has a fixed size, while a vector can grow or shrink dynamically. Vectors also provide many built-in functions for managing elements and memory.

Is a vector slower than an array?

Element access is generally just as fast because vectors use contiguous memory. However, adding elements can occasionally trigger a reallocation, which temporarily costs more time.

What is the difference between push_back() and emplace_back()?

push_back() inserts an existing object into the vector. emplace_back() constructs the object directly inside the vector, which can avoid unnecessary copies or moves for complex types.

When should I use at() instead of []?

Use at() when you want bounds checking and safer code. Use [] when you are confident the index is valid and need the fastest possible access.

How do I remove all elements from a vector?

Call clear() to remove all elements. If you also want to reduce unused capacity, you can additionally use shrink_to_fit() (though the standard does not guarantee that memory will be released).