# Overview > Smart pointers is wrapper class of normal pointer, which automatically release itself (by calling destructor) once **it goes out of scope**. Scope here might be inside a function. Hold on. Excuse me, what? We should not take it like this. Normal classes' destructors are also called once getting out of scope. Smart pointers like `shared_ptr, unique_ptr, etc.` are just part of them. **The essence is that smart pointers are intended to be used as local variables** (in most cases, like class members or function variables). And similar to any other class variable allocated this way - which lifetime is **tied** to some sort of scope, the destructor will be called when it goes out of scope. This follows the principle of RAII. ## RAII *They say RAII is probably the best feature of C++.* > Resource Acquisition Is Initialization (RAII) is a C++ programming technique which **binds the life cycle of a resource** to **the lifetime of an object**. The **resource** here might be heap memory, thread of execution, open socket, open file, locked mutex, disk space, data base connection - which must be acquired before use. To put it simply, the **lifetime** of an object refers to the period of time between object's construction and destruction. **Lifecycle** of a resource refer to the period of time between resource's **allocation** and **de-allocation** (free it, sort of). This is how it goes: > Allocate -> Object Constructor -> ... -> Object Destructor -> De-allocate **Why: Safer memory usage, better performance without any garbage collection overhead**πŸ˜‚ Without this, class destructor must implement memory release operation itself, and that tends to leave bugs due to bad coding practices. **Standard lib:** `std::vector`, `std::string`, smart pointers and `std::lock_guard`, `std::unique_lock`, etc. to manage mutexes. Read more: [https://en.cppreference.com/w/cpp/language/raii](https://en.cppreference.com/w/cpp/language/raii) <!-- ### Stack unwinding (C++ only) --> ## What is scope anyway How do compiler define "scope"? How does it know when a smart pointer goes out of scope and insert some code to call the smart pointer destructor at that place? > The same way everything in C++ knows it's gone out of scope - it reaches the end of the surrounding pair of {}s (or, if it is a member variable of another struct, the destructor of the parent will call the destructors of all child elements). ```cpp!= #include <memory> struct foo { std::unique_ptr<int> p; }; int main() { std::unique_ptr<int> a; { std::unique_ptr<int> b; } // b now goes out of scope and is destructed { foo f; } // f now goes out of scope, meaning both f and f::p are destructed } // a now goes out of scope and is destructed ``` ### What if we attempt to call the destructor? Compiler automatically inserts code to call to object destructor. Now we would like to make a call to destructor right before the end of function. It would result in calling destructor twice. **If the destructor is responsible for releasing some sort of memory, memory corruption would happen.** # Smart Pointers Now get back to smart pointers. Once the destructor is called, the wrapped object's destructor is also called. - `~unique_ptr()` immediately calls the object's destructor. - `~shared_ptr()` only decreases the `use_count` (reference count) by one until it reaches `-1` and then call the wrapped object's destructor later on. ## `std::shared_ptr<T>` internals > Several shared_ptr instances can share the management of an object's lifetime **through a common control block**. The managed object is deleted when the last owning shared_ptr is destroyed (or is made to point to another object). Memory management by `shared_ptr` is **deterministic** because the timing of a managed object's destruction is predictable and **in the developer's control**. Well, but how is it implemented? A `shared_ptr` contains 2 pointers (hence size is 16 in `x86-64`). ![control_block](https://hackmd.io/_uploads/rkEEeY5wA.png) In this section, we'd like to focus on the second one - the pointer to **control block**. The first pointer is the raw pointer of the object being wrapped - returned by `get()` method. More more information, read [Pwning in C++](https://hackmd.io/@trhoanglan04/pwningCpp) (TiαΊΏng Việt). An array of `shared_ptr<IOSymbol>` looks like this in memory: ![shared_ptr_array](https://hackmd.io/_uploads/rkVofd5PC.png) Notice that: - The `vtable` address are the same for all `share_ptr<IOSymbol>` instances (0x555555568860 and 0x5555555686d0). However, the control block addresses are different **heap address** due to the default `std::allocator`. The allocator can be specified. ``` pwndbg> p (std::shared_ptr<IOSymbol>) *0x7ffff7a28058 $4 = { <std::__shared_ptr<IOSymbol, (__gnu_cxx::_Lock_policy)2>> = { <std::__shared_ptr_access<IOSymbol, (__gnu_cxx::_Lock_policy)2, false, false>> = {<No data fields>}, members of std::__shared_ptr<IOSymbol, (__gnu_cxx::_Lock_policy)2>: _M_ptr = 0x7ffff7fa7050, _M_refcount = { _M_pi = 0x5555555e7090 } }, <No data fields>} ``` - This is how the control block looks like: ``` pwndbg> telescope 0x5555555e7250 3 00:0000β”‚ 0x5555555e7250 β€”β–Έ 0x5555555686d0 (vtable for std::_Sp_counted_ptr<IOSymbol*, (__gnu_cxx::_Lock_policy)2>+16) β€”β–Έ 0x55555555b162 (std::_Sp_counted_ptr<IOSymbol*, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr()) β—‚β€” endbr64 01:0008β”‚ 0x5555555e7258 β—‚β€” 0x100000002 02:0010β”‚ 0x5555555e7260 β€”β–Έ 0x7ffff7fa73d0 β€”β–Έ 0x555555568828 (vtable for IOSymbol+16) β€”β–Έ 0x5555555576fa (IOBytes::compare(ArrayTemplate<unsigned char> const&)) β—‚β€” endbr64 ``` What is that `0x100000002`? Weak count and ref count right? ### Operators - Assign: [std::shared_ptr<T>::operator=](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator%3D) If the current pointer points to an existing object before the assignment, that existing object would be destroyed and the new object being assigned's `share_cnt` (reference count) is increased. - Swap: [std::shared_ptr<T>::swap](https://en.cppreference.com/w/cpp/memory/shared_ptr/swap) ## `std::weak_ptr` # Pwn it πŸ’ # Rust # Summary # References - [Reddit: how_does_a_smart_pointer_know_when_its_gone_out/](https://www.reddit.com/r/cpp_questions/comments/ecmzat/how_does_a_smart_pointer_know_when_its_gone_out/) - [shared_ptr - basics and internals with examples](https://www.nextptr.com/tutorial/ta1358374985/shared_ptr-basics-and-internals-with-examples)