The Standard Template Library (STL) provides ready-made, heavily-optimized generic containers, so C++ code rarely needs to implement its own dynamic arrays or linked lists from scratch.
#include <vector>
#include <map>
#include <set>
std::vector<int> nums = {1, 2, 3}; // dynamic array — grows automatically
nums.push_back(4); // add to the end, O(1) amortized
std::map<std::string, int> ages; // sorted key-value pairs, O(log n) operations
ages["Rahim"] = 25;
ages["Karim"] = 30;
std::set<int> uniqueNums = {5, 1, 5, 3}; // stores {1, 3, 5} — duplicates removed, sorted
std::unordered_map<std::string, int> fastAges; // hash map, O(1) average operations
std::vector is the default choice for most sequential data — a dynamically-resizing array with O(1) random access and O(1) amortized append at the end, though inserting/removing from the middle or front is O(n). std::map keeps keys in sorted order (implemented as a balanced tree, giving O(log n) lookup/insert), while std::unordered_map (a hash table) trades that ordering away for O(1) average-case operations — pick map when order matters or you need range queries, unordered_map when raw lookup speed matters more.
std::set/std::unordered_set are the container-equivalent of a map with no separate value — just a collection guaranteeing uniqueness (and, for set, sorted order). Other useful containers include std::list (doubly-linked list, O(1) insert/erase anywhere but no random access), std::deque (double-ended queue, efficient push/pop at both ends), and std::stack/std::queue/std::priority_queue (adapters restricting how an underlying container can be accessed, matching their real-world namesakes).
Choosing the right container is one of the most consequential decisions in C++ code — the same logical operation (say, "look something up by key") can be O(1), O(log n), or O(n) purely based on container choice, so it's worth knowing each container's actual complexity guarantees rather than defaulting to whichever one feels most familiar.