The standard library and smart pointers
Containers, algorithms and ownership: choose the right container and make leaks structurally impossible.
Containers
| Container | Backing structure | Use it for |
|---|---|---|
std::vector | Contiguous array | The default choice: iteration, indexing, cache-friendly storage |
std::array | Fixed-size array | A size known at compile time, with no heap allocation |
std::string | Owned character buffer | Text: it knows its length, unlike a char* |
std::deque | Chunked array | Fast push and pop at both ends |
std::map / std::set | Balanced tree | Ordered keys, range queries, O(log n) lookup |
std::unordered_map | Hash table | Average O(1) lookup with no ordering requirement |
std::span (C++20) | Non-owning view | Passing a range into a function without copying |
#include <map>
#include <unordered_map>
#include <vector>
std::vector<int> v{ 5, 3, 9 };
v.push_back(7);
v.reserve(100); // one allocation instead of several
int first = v.front();
v.erase(v.begin()); // erasing shifts the elements after it
std::map<std::string, int> ordered;
ordered["ada"] = 36; // inserts on first use
ordered.insert_or_assign("grace", 45);
for (const auto &[key, value] : ordered) { // structured binding
std::cout << key << " " << value << "\n";
}
std::unordered_map<std::string, int> fast; // same interface, no orderingAlgorithms and lambdas
The algorithms in <algorithm> work on any pair of iterators, so one call serves a vector, an array or a map.
#include <algorithm>
#include <numeric>
std::sort(v.begin(), v.end());
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // descending
auto it = std::find_if(v.begin(), v.end(),
[threshold = 5](int x) { return x > threshold; });
auto total = std::accumulate(v.begin(), v.end(), 0);
// capture by reference to update something outside the lambda
int seen = 0;
std::for_each(v.begin(), v.end(), [&seen](int &x) { x += ++seen; });
// erase-remove: an algorithm cannot resize the container by itself
v.erase(std::remove_if(v.begin(), v.end(),
[](int x) { return x % 2 == 0; }),
v.end());- A lambda's capture list decides what it may touch:
[x]copies,[&x]refers, and[&]captures everything by reference — which risks a dangling reference if the lambda outlives the scope. - Algorithms never change a container's size, hence the erase-remove idiom.
- A comparator must be a strict weak ordering: using
<=instead of<is undefined behaviour. std::rangesin C++20 lets you writestd::ranges::sort(v)without naming iterators.
Smart pointers
#include <memory>
// unique ownership: exactly one owner, released when it goes out of scope
auto buf = std::make_unique<Buffer>(1024);
buf->fill();
// shared ownership: reference counted, released when the last owner goes
std::shared_ptr<Config> cfg = std::make_shared<Config>("app.conf");
auto copy = cfg; // the count is now 2
std::weak_ptr<Config> observer = cfg; // does not keep it alive; check with lock()
if (auto locked = observer.lock()) {
locked->reload();
}
// a factory that returns unique ownership
std::unique_ptr<Shape> make(const std::string &kind) {
if (kind == "circle") return std::make_unique<Circle>(1.0);
return std::make_unique<Square>(2.0);
}
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(make("circle")); // moves the pointer, does not copy it⚠️
Never build two
shared_ptrs from the same raw pointer, and prefer std::make_unique and std::make_shared over new: they are exception-safe and cannot get the ownership count wrong. Reach for shared ownership only when ownership genuinely is shared, because the count has a cost.FAQ
When is a raw pointer still appropriate?
For non-owning observation, usually as a parameter. The problem is owning raw pointers: any
new you write should normally be inside a smart pointer, and a class should not need a destructor to delete members.How does <code>shared_ptr</code> create a cycle?
Two objects holding
shared_ptrs to each other keep both counts above zero, so neither is ever released. Break the cycle by making one direction a weak_ptr.Related
Classes, constructors and RAII Modern C++ basics
Last refreshed 2026-09-18.