# Smart pointers and owning views ## Unique ownership is the default The Core Guidelines (R.20, R.21) require that a heap object have exactly one owning smart pointer. `std::unique_ptr` satisfies this rule. It holds a pointer, destroys the object when the `unique_ptr` itself is destroyed, and can be moved but never copied. The move operation transfers the stored pointer and leaves the source empty. ```cpp {{#include ../../examples/ch06/ch06_tree.cpp}} ``` In the tree example the root is a `std::unique_ptr`. Each node stores its children in a `std::vector>`. Because every child is owned uniquely, the destruction of the root automatically destroys the whole subtree recursively. No manual `delete` appears, and the program cannot accidentally copy a node-owner. The compiler rejects any copy of a `unique_ptr`. ## Transfer of ownership by value A function that receives a `unique_ptr` by value *takes* ownership. The caller must move the pointer into the parameter. After the call the caller’s pointer becomes empty. This pattern appears in many factory functions: ```cpp std::unique_ptr make_root(int v) { auto p = std::make_unique(); p->value = v; return p; // move-return, caller receives ownership } ``` In the tree example the statement `auto root = std::make_unique()` creates the sole owner. When `main` ends, `root` goes out of scope, the move-return chain unwinds, and the destructor of each `unique_ptr` in the vectors frees the corresponding child. No memory leaks survive past `main`. ### `std::make_unique` versus `new` The guidelines (R.23) require that a `unique_ptr` be created with `std::make_unique` rather than a raw `new` expression. `std::make_unique(args...)` constructs the object and wraps it in a `unique_ptr` in one step. This form is shorter and safer than the two-step alternative. The two-step form first calls `new T(args...)` and then passes the result to the `unique_ptr` constructor. If an exception occurs between these two steps, the raw pointer leaks. `std::make_unique` avoids this window, because the construction and the wrapping happen together. The function also deduces the type, so the code does not repeat the type name. Use `std::make_unique` whenever the object is created and owned immediately. Reserve a raw `new` for the rare case where a custom deleter or a pre-existing pointer is required. Move semantics give `unique_ptr` its efficiency. Moving a `unique_ptr` transfers the pointer without copying the pointed-to object. The operation is a simple pointer assignment plus a null-out of the source. It never allocates and never touches the heap object. This property lets a function return a `unique_ptr` by value at no cost. The move-return in `make_root` does not copy the tree. It hands the same object to the caller. The same reasoning applies when a `unique_ptr` moves into a container or into another owner. ## Shared ownership is a cost-aware choice `std::shared_ptr` holds a control block with an atomic reference count. Every copy increments the counter. The last copy decrements to zero and destroys the object. The guidelines (R.22) caution that `shared_ptr` must be used *only* when at least two distinct owners truly need to keep the object alive. The cost model is higher than `unique_ptr`: | Aspect | `unique_ptr` | `shared_ptr` | |---|---|---| | Allocation | one block (object) | two blocks (object + control) | | Reference count | none | atomic increment/decrement on every copy | | Size of pointer object | ≤ sizeof(void*) | ≈ 2 × sizeof(void*) | | Cache behavior | contiguous access | indirect control block | If the program never needs more than one owner, `unique_ptr` is the zero‑overhead choice. `shared_ptr` allocates an extra control block and incurs atomic reference‑count updates on each copy, doubling pointer size and reducing cache locality. Use `shared_ptr` only when at least two owners truly need to keep the object alive. Otherwise the extra cost is unnecessary. ## Weak pointers break cycles `std::weak_ptr` provides a non‑owning view that does not affect the reference count, breaking reference cycles. Use `lock()` to obtain a temporary `shared_ptr` if the object is still alive. Otherwise `lock()` returns an empty pointer. `expired()` reports whether the object has been destroyed. In the graph example, `weak_ptr` allows the parent link to be observed without extending the child’s lifetime, preventing a cycle. ```cpp {{#include ../../examples/ch06/ch06_graph_weak.cpp}} ``` ## `gsl::owner` documents raw owning pointers Sometimes a C-language API requires a raw pointer that **owns** the pointed-to object. The Guidelines Support Library provides `gsl::owner` as a type alias that makes the ownership intent explicit to static analysis tools: ```cpp void c_api(gsl::owner p); // p must be freed by the caller ``` `gsl::owner` is a type alias that documents raw owning pointers for static analysis tools such as Clang's lifetime‑safety analysis. It carries no runtime cost and does not alter program behavior. A function taking a `gsl::owner` parameter signals that it assumes ownership. A function returning `gsl::owner` signals that it transfers ownership to the caller. Static analysis can verify that each owner releases the object exactly once. ## When pointers are the wrong tool Ownership and lifetime are not the only reasons to use a pointer. Frequently a value, a `std::span`, or a `std::string_view` conveys the required relationship without any ownership semantics. * **Value**: use when the object’s lifetime is confined to the current scope and copying is cheap. * **`std::span`**: a non-owning view over a contiguous range. It is ideal for passing array slices to functions. * **`std::string_view`**: a read-only view of a string. It is perfect for read-only parameters where the callee must not modify or own the data. Choosing a smart pointer when a simple view suffices adds unnecessary indirection and can hide bugs. The guidelines (R.3) advise to prefer plain values and views first. Smart pointers come into play only when the lifetime must outlive the current scope **and** no value can express the relation. A value owns its data within the current scope. Copying or moving it transfers ownership automatically. `std::span` provides a non‑owning view of a contiguous range, and `std::string_view` provides a read‑only view of a string. These types express the relationship without ownership overhead and avoid the need for raw pointers. Use them before considering a smart pointer. ## Try this Take the tree program from the previous section and add a function: ```cpp int height(const tree_node&); ``` `height` returns the length of the longest root-to-leaf path (the number of nodes on that path). Write the function recursively, using only the `tree_node` interface. When you run the program, observe that the tree is still freed automatically when `main` exits, even though `height` returns a plain `int`. *What does `std::unique_ptr` guarantee about the tree’s memory after `height` returns?*