std::shared_ptr is a smart pointer that allows multiple owners of the same heap-allocated object, tracked through a shared reference count: the object is destroyed only when the last shared_ptr pointing at it is destroyed or reset. It's the right tool when ownership is genuinely shared and there's no single object whose lifetime can be said to own the resource, unlike unique_ptr, which assumes exactly one owner.
std::shared_ptr<Widget> a = std::make_shared<Widget>(); { std::shared_ptr<Widget> b = a; // now two owners, refcount == 2 b->doThing(); } // b destroyed, refcount == 1, Widget still alive because a exists
make_shared allocates two things in a single heap allocation: the Widget object itself, and a control block holding the strong reference count, the weak reference count, and the deleter. Every copy of the shared_ptr increments the strong count (via an atomic operation, since shared_ptr is designed to be safely copied across threads), and every destruction decrements it; the object is destroyed when the strong count hits zero.
shared_ptr copies: Control block (one per object):
a ----\ strong_count: 2
b ----+-----------------> weak_count: 0
deleter
|
v
Widget object
Constructing from a raw pointer instead of make_shared, std::shared_ptr<Widget>(new Widget()), allocates the object and the control block separately, two allocations instead of one, and loses locality between them. make_shared should be preferred by default for this reason, except when a custom deleter or a weak_ptr-only construction path requires the raw-pointer constructor.
Every copy and destruction of a shared_ptr is an atomic increment or decrement, which is real overhead compared to a raw pointer or unique_ptr, especially under contention across threads. shared_ptr is frequently reached for by default in code that doesn't actually need shared ownership, a function parameter that only reads the object should take a reference or raw pointer, not a shared_ptr copy, and a data structure with a single clear owner should use unique_ptr. Reaching for shared_ptr as the default “safe” smart pointer, rather than as the answer to “does this genuinely have multiple owners”, is one of the most common ownership-design mistakes in modern C++ code.