# Effective Modern C++ Notes
WIP
### Prefer nullptr over 0 and NULL
* 0 is an int, but C++ will interpret is as a ptr in context where only ptr is used
* same for NULL, but can be different integral types like long
* 0 and NULL don't have ptr types
* why is this bad? overload resolution is ambiguous
```
void func(int);
void func(bool),
void func(void*);
func(NULL); // either will not compile or call // func(int)
```
* nullptr type is std::nullptr_t, a type of nullptr, and implicity converts to all raw pointers. Using nullptr would fix ambiguity in above ex and call func(void*).
*
### Unique Ptrs
* auto_ptr is unique_ptr for C++ 98 when move semantics did not exist. unused in modern C++
* same size as raw ptr
* exclusive ownership semantics. non-null unique ptr always owns the obj it points to
* moving a unique_ptr transfers ownership, so unique ptr is a move-only type
* upon destruction, destroys its resource
* use case can be factory function return type for objects in a hierarchy
* custom deleter objs, but must define as part of unique ptr (ex. unique_ptr<Obj, customDeleter>)
* cannot assign raw ptr to unique ptr. reset can be used instead
* deleting a derived class object via a base class ptr requires base class to have virtual destructor!
* array, vector, string are better than raw arrays
### Shared Ptrs
* lifetime managed by pointers through shared ownership
* when last shared_ptr stops point to an obj, the shared_ptr destroys the obj it is pointing to
* shared_ptrs consult a resource's reference_count to track how many shared_ptrs pointing to it
* sp1=sp2 points sp1 to the object pointed to by sp2. decrements sp1 obj reference_count and increments sp2 reference count
* Performance
* twice the size of a raw ptr (raw ptr to resource + raw ptr to resource's reference_count)
* reference_count is dynamically allocated
* avoid this cost by using make_shared
* reference count changes are atomic, so reading and writing is more costly
* reference_count is part of a data structure called control block
* control blocks are created during make_shared, shared_ptr created from unique_ptr, shared_ptr constructor is called with a raw ptr
* AVOID creating more than one shared_ptr from the same raw ptr b/c this creates two control blocks, leading to undefined behavior (ex. try to destroy the same obj twice due to two diff reference_counts in two control blocks)
* In general, avoid passing raw_ptr to shared_ptr constructor, use make_shared instead OR pass definition of raw ptr (ex. new Widget) directly into shared_ptr constructor to avoid re-using same raw ptr for another shared_ptr constructor
* enable_shared_from_this -> template for base class to create shared_ptr from this ptr
* moving a shared_ptr is faster than copying since reference_count does not need to be updated
* Specifying custom deleter doesn't change size of shared_ptr
* unique_ptr -> shared_ptr is easy, but reverse is not possible, even if reference_count is 1. If exclusive ownership is possible, go with unique_ptr
### Weak ptrs
* special type of shared ptr for pointers that can dangle (obj gets destroyed while ptr is pointing to it)
* cannot be dereferenced, increment smart ptr counter, or tested for nullness
* created from shared_ptrs, point to same obj, but don't affect reference_counter
* weak ptr expired function can check if obj has been destroyed
* What if we want to check and return dereferenced obj?
* atomic operation. otherwise a thread can destroy a shared_ptr, causing obj to be destroyed after the check has occurred on another thread
* create shared ptr from weak ptr using weak ptr lock function (returns null if expired) OR pass weak ptr into shared_ptr constructor (if expired, exception is thrown)
* use case:
* store weak_ptrs in a cache while shared_ptrs are dealt with
* subjects (state may change) hold container of weak ptrs to observers (notified by subject state change) to figure out if observers have been destroyed
* Consider A <-> B -> C. A -> B and B -> C is shared_ptr. What should B -> A be?
* If raw_ptr, if A destroyed, then B -> A is dangling which is bad
* If shared_ptr, A <-> B ptrs cannot be destroyed b/c reference_count is 1 for both. Leak.
* If weak_ptr, if A destroyed, then B -> A can check if dangling first
* same as shared_ptr efficiency_wise
### Prefer make functions over new
* Make functions take an arbitrary set of args, perfect-forward them to constructor for dynamically allocated obj and return smart ptr for that obj
* examples: make_shared, make_unique, allocate_shared
* allocated_shared is like make_shared but first arg is an allocator obj for dynamic memory allocation
* Why use make functions?
* make functions don't repeat type, which can reduce code duplication
* compilers are not required to generate code that executes in a specific order. using new can create a obj that is leaked if created directly as an shared ptr arg in a function. using make_shared means shared_ptr is created once make_shared is invoked. unique ptr equivalent is the same way
* for make_shared and allocate_shared, new does two dynamic allocations -> for obj and control block. using make does one dynamic allocation -> memory holds obj + control block
* Why not for all?
* can't specify custom deleters
* perfect forwarding code uses paranthesis, not braces, so cannot use a braced initializer for make functions
* Workaround: use auto as type for braced initializer list. then pass variable into make function
* Why not for shared specifically?
* control block is in same mem chunk as obj, so if obj destroyed, memory cannot be deallocated until control block is destroyed too. ex. weak ptrs use weak_count in control block so if all shared_ptrs are destroyed but weak ptr still exists, memory cannot be deallocated
* when using new, pass result into smart pointer constructor that does nothing else. this may result in passing in a lvalue instead of an rvalue shared ptr into a construcotr. lvalue requires copying. rvalue can be moved. so need to call move on lvalue to turn it into a rvalue.