Smart pointers make ownership visible in an interface. They do not replace references and raw pointers used solely to borrow an object whose lifetime is guaranteed elsewhere. Select the pointer type from the lifetime contract, then make transfer and observation explicit at API boundaries.
Unique Ownership #
std::unique_ptr<T> owns one object and is move-only. Create it with std::make_unique when ordinary new and the default deleter are appropriate; pass or return it by value to transfer ownership.
#include <memory>
std::unique_ptr<Connection> open_connection(Address address);
void install_connection(std::unique_ptr<Connection> connection);
install_connection(open_connection(address));A custom deleter is part of unique_ptr’s type, so it can affect its size and the types accepted by an API. The deleter must tolerate the ownership protocol and must not throw from destruction.
A unique_ptr<T> may appear in a header while T is incomplete. The default deleter, however, requires T to be complete where operations that destroy the object are instantiated, commonly the owning class’s out-of-line destructor. unique_ptr<T[]> represents an array allocation but does not retain its length; prefer std::vector<T> for an owned sequence.
Shared Ownership and Observation #
std::shared_ptr<T> shares a control block containing ownership state and, commonly, the deleter and allocator. Copying a shared_ptr extends ownership; the last owning pointer destroys the managed object. std::make_shared can allocate the object and control block together, reducing allocations. Because weak observers retain the control block, that combined allocation can keep the object’s storage unavailable until the observers disappear.
The aliasing constructor shares an existing control block while storing a different pointer:
auto owner = std::make_shared<Record>();
std::shared_ptr<Member> member(owner, &owner->member);member.get() points at the subobject, while lifetime is controlled by owner’s Record. Do not construct unrelated shared_ptr instances from the same raw pointer; each would believe it independently owns that object.
std::weak_ptr<T> observes a control block without extending object lifetime. Use lock() to obtain a temporary owning pointer before dereferencing. Model back edges and caches with weak_ptr when ownership cycles would otherwise keep an object graph alive indefinitely.
API Contracts #
Use signatures that state the intended relationship:
void take(std::unique_ptr<Job>); // transfers unique ownership
void share(std::shared_ptr<const Config>); // retains shared ownership
void inspect(const Config&); // borrows; non-null
void maybe_inspect(const Config*); // borrows; nullable
Accept const std::shared_ptr<T>& only when an operation needs neither to retain ownership nor to expose an ownership contract; accepting T& or T* documents borrowing more directly. A borrowed pointer or reference remains valid only for the lifetime promised by its caller.
Comments
Read or comment on GitHub (sign-in required to comment)