Skip to content

C++ Vectors: How It Works And Why You Should Use Them

There are different ways you could store a sequence of values in C++. This post will dive into “vector”, which is undoubtedly the most flexible and performant container used to store many, contiguous values in C++.

The Need For Sequences In Programming

When learning C++, we often start learning the simple built-in types such int, char, float, double, etc. We can go quite far representing real-world concepts with these types. For example, if you want to model someone’s height in your code, you would probably use a double variable to store that value. What happens if you need to count the cars driving through your street? An int will do.

What happens if we need to model a collection of values, such as a list of books you have read? Naively, having an std::string for each book title you own could be a solution, but this approach would be impractical and unmanageable: What if you need to sort them? How would you represent the ordering? How do you pass these values around efficiently? This is where std::vector shines: you can store item collections, use them with C++ standard algorithms (such as std::sort and std::transform), and have the efficiency of contiguous memory.

In addition to improving a simple C++ program with the points made above, there are other motivations for using std::vector such as:

  • Flexible item collections, where you can add and remove items.
  • Iterating over collections of data and applying logic to each item.
  • User-friendly, easier replacement for the standard C arrays.
  • Passing arrays of items as parameters to functions.

How Do We Define A Vector? Why Does The Syntax Look “Different”?

If you are a beginner CPP programmer, the type declaration above probably looks a little bit odd and intimidating. But do not fear, we will decompose the declaration of vectors and explain what every element present in the creation of vectors mean.

suppose you want to create an array/collection of integers. The following code block shows a typical example of the declaration of a std::vector storing integers.

#include <iostream>
#include <vector>
#include <string>

int main()
{
  std::vector<int> days_in_each_month = {
    31,  // January 
    28,  // February
    31,  // March
    30,  // April
    31,  // May
    30,  // June
    31,  // July
    31,  // August
    30,  // September 
    31,  // October
    30,  // November
    31   // December
  };

  std::cout << "There are "
            << std::to_string(days_in_each_month[0])
            << " days in January.\n";
}

Explaining The Definition Of Vector

The program in the example above creates the variable days_in_each_month, an std::vector<int> , or a vector of integers, storing the number of days per month in a usual year. The program then prints the number of days in January by accessing the first position representing January in the vector.

In line 1, we import the header <vector> that you must include if you wish to use vectors in your code. This header will define the type std::vector<T> , or in other words, a type vector that can store general types (the <T> part says that std::vector is a templated type). If you are a beginner, it means whatever type you place between the diamonds <> in the vector declaration will be the type of each element in your vector. For example, std::vector<std::string> is a vector of strings, std::vector<double> is a vector of doubles. If you’re an experienced programmer, you should already know what templates are!

In addition, the prefix “std::” tells us that the vector<T> type lives inside the “stdnamespace. This is a common practice in the CPP standard library (or “stl”) headers such as <vector>. If you haven’t yet, you will certainly come across many types and functions from the standard library as they are very useful in everyday C++ development.

Alternative Ways To Define Vectors

There are more ways to define vectors, the example below will show another couple of ways of using vectors in your code.

#include <iostream>
#include <string>
#include <vector>

/// This function takes a vector and returns the
/// sum of all its elements.
int sum_of_elements(std::vector<int> const& elements)
{
  int sum = 0;
  for (auto const& element : elements)
    sum += element;
  }
  return sum; 
}

int main()
{
  // Empty vector of strings representing
  // my friend list
  std::vector<std::string> friend_list;
  
  // Add each of my friends to the vector
  friend_list.push_back("Bob");
  friend_list.push_back("Pickles");
  std::cout << "I have " << std::to_string(friend_list.size())
            << " friends.";
}

We can see that we can easily pass vectors to functions as arguments. In addition, you can also modify the vector once it has been created. In our case, we added elements to an empty vector using push_back.

In fact, std::vector has quite a few member functions you can call. Some of the notable ones are:

  • push_back(element) adds element to the end of the vector.
  • at(n) gets the element at the nth position of your vector.
  • size() returns the current size of your vector.
  • empty()returns true if the vector is empty, falseotherwise.
  • erase(iterator) erases the element in the vector through its iterator.

If you want to find out what other functions std::vector offers, and how to use them, you should have a look at the vector CPP reference. Honestly, you should get used to reading the CPP reference if you are just starting out with C++, it’s a very useful resource!

How Does Vector Work?

Interestingly, every time you create a variable in C++, the compiler will work out how to allocate memory in your computer and produce the corresponding machine code to do so. For example, when creating an int, the corresponding machine code will “ask” the computer to reserve a few bytes in RAM to store your integer. Not surprisingly, this same “memory allocation” pattern is used for vectors.

A Very Basic Model Of Vectors

Under the hood, an std::vector<T>is just references to the starting place in memory where the first value is and where the last value of the vector. All the elements in a vector are stored contiguously, meaning that the bytes used to represent the next element is stored directly after the previous one. Hopefully, Figure 1 gives you a better idea of how a vector is stored in your computer.

CPP vectors representation. How a vector is represented in memory.
Figure 1: How a vector is represented. On the left is the data stored for a vector (the start and end positions of the memory). On the right is an example of the contents of computer memory, with the vector data in there.

Note that, as hinted by Figure 1, an std::vector<T> only needs to keep track of two values: the start and end places (positions) in memory of the vector. Therefore, the image above portrays a vector that starts in memory at position 3 and the last element is located at position 7. This gives us the vector storing the values{27, 2, 102, 57, 42} .

The point here is that vectors only need to keep track of the start and end values. Obviously, other properties — such as size — can be “worked out” from the start and end.

What About The “Flexibility” Of Vectors?

You may have notices that the model I provided in the previous section does not allow for shrinking and growing. In an attempt to keep things simpler, I omitted the information on “capacity”. The C++ vector also keeps track of the maximum capacity a vector can have, so when a user tries to push_back an element that is going to bring the size above the maximum capacity, the vector will grow automatically! This is memory management at its best, something you would have to implement yourself in C.

It is generally not good practice to rely on implementation details when using the C++ standard, but I thought having a glimpse into how the computer stores and manages a vector can help you better understand vectors and its functions.

Choose Vectors Over Other Containers

In most cases, when a collection of items is needed in C++, choose vectors. Due to the elements being stored contiguously, std::vector is extremely performant compared to other STL containers (such as std::list<T>). CPU caching is the main reason for the improvement in performance.

Not only is performance what you get from vectors, but also ease of use. Compared to old-style, outdated C-arrays, vectors are much nicer to create, work with and pass around. In addition, management of the memory comes for free: whether you need to increase the size or shrink the container.

I hope you will start using vectors more often (when necessary). Similarly, if you are a seasoned developer, I hope you ditch the old ways of creating arrays for vectors, it really will improve your productivity.

TL;DR

Do you need a collection or array of items in C++? Use std::vector.

Do you need to add or remove elements from your array? Use vectors.

Unless you know you absolutely don’t need a vector as your container, choose vectors. They are efficient and flexible. Moreover, you don’t have to worry about writing code to manage the underlying memory.

Did I miss anything? Have you got any suggestions or feedback? Feel free to drop a comment below!

Published inCPP

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *