We've seen in the preceding chapters that C++ has a love-hate relationship with dynamic memory allocation.
On one hand, dynamic memory allocation from the heap is a "code smell"; chasing pointers can hurt a program's performance, the heap can be exhausted unexpectedly (leading to exceptions of type std::bad_alloc), and manual memory management is so subtly difficult that C++11 introduced several different "smart pointer" types to manage the complexity (see Chapter 6, Smart Pointers). Successive versions of C++ after 2011 have also added a great number of non-allocating algebraic data types, such as tuple, optional, and variant (see Chapter 5, Vocabulary Types) that can express ownership or containment without ever touching the heap.
On the other hand, the new smart pointer types do effectively manage the complexity of memory management...