Back to C++
2026-04-199 min read

Containers library (C++)

Learn Containers library (C++) step by step with clear examples and exercises.

Why This Matters

Understanding the C++ Containers Library is essential for any serious C++ developer. The library offers a collection of class templates and algorithms that simplify the implementation of various data structures, making your life as a programmer easier. By mastering this library, you'll be able to write more efficient, maintainable, and bug-free code in your projects.

The Containers Library is part of the Standard Template Library (STL) in C++, providing generic data structures and algorithms to manage collections of elements efficiently. This library offers three main categories of containers: sequence containers, associative containers, and unordered associative containers. Each container type has its own strengths and weaknesses, making it essential to choose the appropriate one for your specific use case.

Prerequisites

To fully grasp the concepts covered in this lesson, you should have a good understanding of:

  1. Basic C++ syntax and programming concepts (variables, functions, loops, etc.)
  2. Object-oriented programming principles
  3. Understanding of templates and classes in C++
  4. Familiarity with STL iterators and algorithms
  5. Knowledge of exception handling and memory management in C++
  6. Understanding of the C++ Standard Library headers (`, , `, etc.)

Core Concept

The Containers Library is a part of the Standard Template Library (STL) in C++, providing generic data structures and algorithms to manage collections of elements efficiently. The library offers three main categories of containers: sequence containers, associative containers, and unordered associative containers.

Sequence Containers (Expanded)

Sequence containers implement data structures that can be accessed sequentially. They include:

  1. array (C++11) — a fixed-sized, contiguous array
  2. vector (C++11) — a resizable, contiguous array
  3. inplace_vector (C++26) — a resizable, fixed capacity, inplace contiguous array
  4. hive (C++26) — a collection that reuses erased elements

Sequence containers offer constant-time access to elements using iterators and have efficient operations for adding and removing elements at the end of the container. However, inserting or removing elements in the middle of a sequence container can be costly due to the need to shift other elements.

Associative Containers (Expanded)

Associative containers provide fast access to elements based on their keys. They include:

  1. set — an associative container that contains only unique elements, sorted in ascending order
  2. multiset — an associative container that can contain duplicate elements, sorted in ascending order
  3. map — an associative container that stores key-value pairs, with the keys sorted in ascending order
  4. multimap — an associative container that can store multiple values for a single key

Associative containers offer fast lookup and insertion of elements based on their keys, making them ideal for applications where you need to quickly access specific data. However, they may not be as efficient in terms of memory usage or iteration performance compared to sequence containers.

Unordered Associative Containers (Expanded since C++11)

Unordered associative containers offer fast access to elements based on their hash values instead of sorting them. They include:

  1. unordered_set — an unordered associative container that contains only unique elements
  2. unordered_multiset — an unordered associative container that can contain duplicate elements
  3. unordered_map — an unordered associative container that stores key-value pairs
  4. unordered_multimap — an unordered associative container that can store multiple values for a single key

Unordered associative containers offer constant-time lookup and insertion of elements, making them ideal for applications where you need fast access to specific data without the need for sorting. However, they may consume more memory compared to their ordered counterparts due to the need for additional data structures like hash tables.

Worked Example

Let's dive into a practical example using the vector container.

#include <iostream>
#include <vector>
#include <algorithm> // For sort() function

int main() {
std::vector<int> numbers;

// Inserting elements into the vector
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);

// Accessing elements using iterators
std::vector<int>::iterator it = numbers.begin();
std::cout << *it << std::endl; // Output: 1

// Iterating through the vector
for (it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl; // Output: 1 2 3

// Sorting the vector using STL's sort() function
std::sort(numbers.begin(), numbers.end());

// Accessing and printing sorted elements
it = numbers.begin();
std::cout << *it << std::endl; // Output: 1
for (it++; it != numbers.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl; // Output: 1 2 3

return 0;
}

In this example, we created a vector of integers and inserted three elements using the push_back() function. We then accessed the first element using an iterator and printed all elements by iterating through the vector. After that, we sorted the vector using STL's sort() function and printed the sorted elements.

Common Mistakes

  1. Forgetting to include the necessary headers — Always make sure to include the appropriate header files, such as ``, at the beginning of your C++ source files.
  2. Misusing iterators — Be careful when using iterators, as they can point to invalid positions in the container if you modify the container while iterating over it.
  3. Incorrectly initializing containers — Make sure to properly initialize your containers, such as setting an initial size for vector or providing a custom allocator for more complex data structures.
  4. Ignoring capacity and performance considerations — Be aware of the capacity and performance characteristics of different containers when choosing the appropriate one for your use case.
  5. Not handling exceptions properly — When using containers that can throw exceptions, such as vector::resize(), make sure to handle exceptions appropriately to ensure your program's stability and robustness.
  6. Confusing iterators from different containers — Be careful when mixing iterators from different containers, as they may have different types and behaviors.
  7. Not using const-correctness — When working with containers, make sure to use const-correctness to ensure that your code is more efficient and less prone to errors.
  8. Not optimizing for memory usage — Be aware of the memory usage characteristics of different containers when choosing the appropriate one for your use case, especially in applications where memory usage is a critical concern.
  9. Ignoring container-specific algorithms — STL provides various algorithms that are optimized for specific containers, such as std::sort() for sorting vector or std::find_if() for finding elements in a container. Make sure to use these algorithms when appropriate.
  10. Not taking advantage of move semantics — When working with containers that support move semantics, such as vector, make sure to take advantage of them to improve the performance and efficiency of your code.

Practice Questions

  1. What is the difference between a set and an unordered_set in C++?
  2. How can you efficiently find an element in a vector using iterators?
  3. What is the time complexity of inserting an element at the beginning of a list?
  4. Why would you choose to use a map over an unordered_map for certain applications?
  5. How can you efficiently remove an element from a vector without affecting its order?
  6. What is the time complexity of inserting an element at the end of a vector?
  7. What is the time complexity of finding an element in a sorted vector using binary search?
  8. What is the difference between a deque and a list in C++?
  9. How can you efficiently insert an element at a specific position in a vector without shifting other elements?
  10. What is the time complexity of finding an element in an unsorted vector using linear search?

FAQ

  1. Why should I use the Containers Library instead of implementing my own data structures?

Using the Containers Library allows you to use pre-written, optimized implementations of common data structures, saving you time and ensuring better performance. The library offers a wide range of containers that cater to various use cases, making it easier for developers to choose the appropriate one for their specific needs.

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

A vector is a dynamic array that can resize itself as elements are added or removed, while an array has a fixed size at creation. This means that a vector offers more flexibility in terms of capacity management, but may have slightly slower access times compared to an array.

  1. Can I use iterators with all types of containers in the Containers Library?

Yes, most containers in the Containers Library support iterators, allowing you to access and manipulate their elements efficiently. However, some containers, such as stack and queue, do not directly expose iterators but provide other ways to access their elements.

  1. What is the time complexity of inserting an element at the beginning of a vector?

Inserting an element at the beginning of a vector has a linear time complexity of O(n), as all other elements need to be shifted to make room for the new element. To improve performance, consider using a list or a custom data structure if inserting elements at the beginning is a common operation in your application.

  1. How can I efficiently find an element in a large unordered_map or unordered_set?

Use the built-in functions like find(), which have an average time complexity of O(1) for these unordered associative containers. However, be aware that the worst-case time complexity can still be O(n), so it's essential to choose an appropriate data structure based on your specific use case and expected input distributions.

  1. How can I efficiently remove an element from a vector without affecting its order?

You can use the std::remove() algorithm to remove a specific element from a vector. However, this operation has a linear time complexity of O(n), as all elements after the removed element need to be shifted to fill the gap left by the removed element. To improve performance, consider using a list or a custom data structure if removing elements frequently is a common operation in your application.

  1. What is the difference between a deque and a list in C++?

A deque (double-ended queue) is a sequence container that allows efficient insertion and removal of elements from both ends, while a list is a sequence container that allows efficient insertion and removal of elements at any position. A deque offers faster constant-time access to its front and back elements compared to a list, but may consume more memory due to the need for additional storage for the extra capacity at each end.

  1. How can I efficiently insert an element at a specific position in a vector without shifting other elements?

You can use the std::insert() algorithm to insert an element at a specific position in a vector. However, this operation has a linear time complexity of O(n), as all elements after the insertion point need to be shifted to make room for the new element. To improve performance, consider using a list or a custom data structure if inserting elements at specific positions is a common operation in your application.

  1. What is the difference between a vector and an array when it comes to memory management?

A vector dynamically manages its memory by allocating and deallocating memory as needed, while an array requires manual memory management using new and delete. This means that a vector offers more convenience and safety in terms of memory management but may have slightly slower access times compared to an array.

  1. Why would you choose to use a map over an unordered_map for certain applications?

You might choose to use a map over an unordered_map if your application requires elements to be sorted by their keys, or if you need to iterate through the container in a specific order. However, keep in mind that unordered_map offers faster lookup times due to its hash-based implementation, so it may be more suitable for applications where performance is critical and key ordering is not important.

Containers library (C++) | C++ | XQA Learn