Default to unique_ptr for single ownership. It has no overhead versus raw pointers. When objects have exactly one owner responsible for cleanup, unique_ptr is the right choice. Use shared_ptr when ownership is shared among multiple parts of code.
Caches, observer patterns, and complex object graphs often need shared ownership semantics. Use weak_ptr to observe without owning, typically to break cycles or check if an object still exists.
Start with unique_ptr, move to shared_ptr only when sharing is needed.