Smart Pointers C++

Each of section contains two notes

  1. Interesting and notable notes about such smart pointer
  2. Issue along with detail on using such smart pointer


Note Detail
reset() Destroy managed object, then set managed object pointer to 0.
reset(ptr) Destroy managed object if ptr doesn't equal to its managed object pointer, then set managed object pointer to ptr. ptr is of the same type of managed object.
release() Save a copy of managed object pointer and return, but prior to that set its managed object pointer to 0.
reset(release()) Set managed object pointer to 0 but not destroy it. This semantics means release ownership of managed object. It's called when operator= is called or per say assign new value to it.
operator-> Return pointer to managed object, so we can call managed object's function right after.
operator* Return reference to managed object

Issue Detail
Transfer Ownership When std::auto_ptr gets assigned to a new value, then ownership is transfered to lhs (left-hand side) in which internally it calls reset(release()) which means only reset its managed object pointer but not destroy it.
Not support array of objects Internally it uses delete to destroy managed object when it goes out of scope, if users created object with i.e. new T[10] then program will crash (segmentaion fault).
Not work with standard containers Due to its copy semantics which will transfer ownership to lhs, and release its own ownership. Thus this doesn't work well with standard containers as elements should be able to move around, and are not too early destroyed unexpectedly.


Note Detail
Kindof replacement to std::auto_ptr std::auto_ptr is a valiant attempt to create a std::unique_ptr before C++ had move semantics.
Can be empty It can own no object which called empty.
Requirements It satisfies the requirement of MoveConstructible, and MoveAssignable but not the requirement of either CopyConstructible or CopyAssignable. Notice that the latter twos are marked with = delete in its declarations.
Can't copy but move It only can move, but not copy. As its copy constructor, and copy assignment operator are disabled (marked with = delete). This helps to guarantee that at any moment in time, only one std::unique_ptr owns the managed object.
Support single or array of objects Its constructor supports both of dynamically allocated of a single (via new) or array of objects (via new []).
Can be used in standard containers Compatible to be used with standard containers like std::vector, or others
Exclusive ownership It has an exclusive (only itself) ownership over managed object.


Note Detail
Self-made garbage collector It's based on reference counting. When reference count reaches 0 then managed object get destroyed via (by default) delete.
std::make_shared Whenever possible try to use std::make_shared to create std::shared_ptr, the important thing is it provides efficiency in creating std::shared_ptr to have both managed object living alongside meta bookkeeping data in the same single area of memory space. Anyway, std::make_shared doesn't support custom deleter, thus in that case users fall back to directly use std::shared_ptr's constructor.
Dereferences It provides * and -> for dereference to managed object(s).
unique() A function to check whether the resource being managed is only managed by itself or per se use_count() == 1.
operator bool It can be used to check whether such std::shared_ptr has managed object in managed, or per se *this and get() != nullptr. Example giving p is std::shared_ptr then we can do if (p) { std::cout << "p has managed object and still alive"; }.
Sharing group All std::shared_ptr share the same reference count belong to the same group. std::shared_ptr can be in the same group when it created from std::shared_ptr that point to the same managed object, or being the first one that created directly with such managed object. See 1.
deleter If created from std::unique_ptr, it will get deleter from it.
operator= with move semantic If use operator= with move semantics in which it accepts std::auto_ptr, std::unique_ptr and std::shared_ptr wheres each one can be used to create a new std::shared_ptr object, finally it will assign itself via copy assignment operator. Once you did this, you cannot convert back to the source type.
Lifetime It has both control block and managed object data. Its managed object will be destroyed when its strong reference count reaches 0. But its control block is still around until both strong and weak reference counts reach 0.

Issue Detail
By default, not support array of objects' destruction It uses delete to delete managed object by default. Although users have an option to supply array of objects to be managed by std::shared_ptr, but it's their responsiblity to also supply deleter. This is only possible with std::shared_ptr's constructor not std::make_shared.
1 Multiple std::shared_ptr should not created from the same naked pointer Special care needed here. Problem will arise for the case when multiple std::shared_ptr created from the same naked pointer created elsewhere but not immediately as part of std::shared_ptr's constructor argument. It's ok if one of std::shared_ptr created with such naked pointer, but the less must not do that to avoid multiple time deleting managed object resulting in undefined behavior. Instead the less of std::shared_ptr should created from std::shared_ptr which is created from such naked pointer. Also we should not ever touch that naked pointer directly again, let std::shared_ptr do the job.
Cyclic reference Resources won't be released properly if there is a cyclic reference i.e. object A has its member variable as std::shared_ptr points to object B which in turns also has its member variable as std::shared_ptr points to object A. This creates cyclic reference and will cause memory leak problem. It can be solved by using std::weak_ptr on either object A or B.


Note Detail
Sharing semantics It provides sharing semantics, not owning semantics.
lock() Created std::shared_ptr that shares ownership of managed object. Use this function to gain access to the managed object.
operator= Replace managed object with the one managed by rhs (ride-hand side) of std::weak_ptr. After this, it will shared the same managed object, as well as reference count. It can be used with either std::weak_ptr or std::shared_ptr.
use_count Get number of std::shared_ptr that shares the same managed object.
Memory layout std::weak_ptr has similar memory layout to std::shared_ptr that it stores control block and stored pointer of std::shared_ptr it created from at the same memory block.
Solve cyclic reference std::weak_ptr can be used to solve cyclic reference problem.

Code Examples

Topic URL
std::auto_ptr AutoPtr_SmartPointer.cpp
std::unique_ptr UniquePtr_SmartPointer.cpp
std::shared_ptr SharedPtr_SmartPointer.cpp
std::weak_ptr WeakPtr_SmartPointer.cpp

Misc Notes


Oct, 14, 2019

First published on Sep, 6, 2019

Written by Wasin Thonkaew
In case of reprinting, comments, suggestions
or to do anything with the article in which you are unsure of, please
write e-mail to wasin[add]wasin[dot]io

Copyright © 2019 Wasin Thonkaew. All Rights Reserved.