General libraries: Smart pointers


Ok guys,
Let’s have a discussion about pointers.

 

For lazy people, long story short:

C++11 comes with three new pointer types that wraps over the regular pointer.

unique_ptr: One pointer inside, one ownership of the object. Default one to be used instead of the new operator. The copy constructor / assignment operator are deleted.

shared_ptr: Used when we want to share the ownership, it keeps count of the references, deallocates when the count reaches 0. We can use copy constructor / assignment operator.

weak_ptr: Weak reference to a shared_ptr, does not influence the object lifetime. The shared pointer contained can be removed even if we have the weak_ptr reference. Used to check the state of the object, and can access the object contained by promoting it to a shared_ptr.

For a detailed info, please read the rest of the text.

Until C++11, the only pointer available was the one created using the ‘new’ operator.
The problem with the raw pointers is that the programmer have to manually destroy the object when it no longer uses it (using the ‘delete’ operator).

The smart pointers are classes that wraps over the raw pointers. The container owns the contained pointer and they handle the deallocation automatically. They also allow the developer to get the raw reference of the pointer.

The smart pointer classes contain the following functionality:

  • get() function : which will return the raw pointer it owns upon. The function was introduced for compatibility with older versions / C functions. Weak pointers does not contain this functionality.
    The developer should never delete the raw pointer retrieved through this function.
  • reset() function: release the ownership of the managed object.
  • swap(X) function, where X is of the same type of our wrapper class: Switch the ownership between pointers
  • A conversion to bool that returns the state of ownership:

To be remembered that all these classes are based on templates: using multiple object types will lead to a substantial ammount of code.

 


  • Raw’ pointer (‘new’ operator)

Usage:

Problems encountered:
What does it point to ?
– A local object? The object is destroyed when it goes out of scope.
– A dynamically allocated object? We are not sure who will have to deallocate it.
– The object is stored somewhere else? We are not sure for how long it will be valid.


Unique pointer

Mechanism:
It contains only a pointer member that either points to an object, or it is nullptr.
The destructor simply delete the object (if it exists).

Usage:

  • There is only one ownership
  • The raw pointer cannot be copied / assigned ( The copy constructor / assignment operator are deleted )

Passing the ownership:

The ownership is passed using move constructor / move assignment.

  • Move constructor

Since the return value of a function is an rvalue, the following example enters the move assignment case:

  • Explicit ownership transfer

Note: If each container owns an object, the managed object from the right wrapper WILL be deleted:

 

Ptr1 owns the object previously owned by ptr2, the object previously owner by ptr1 was deleted. ptr2 member will now own nothing.


Shared pointer

Mechanism:
It is a reference-based wrapper – it holds a count that is reflecting the number of objects that point to the managed object (raw pointer) and it will automatically delete it when the count reaches 0.

By default, the shared pointer is a class template that holds two pointers, one of them being the actual object it wraps around, and the other being a pointer to a control block – which is a dynamically allocated object that contains:
– the number of shared pointers that owns the object (the object is removed when this count reaches 0)
– the number of weak pointers that owns the object (does not take them into account)

Construction of the wrapper is done using the following command:

In comparison with unique_ptr, the user is able to share ownership to the same object by passing it using the move constructor (unique_ptr have the move constructor deleted)

Disadvantages:

  • You need extra memory to maintain a reference count and it has to be incremented/ decremented every time you copy or destroy an instance.
  • The reference count manipulation have to be thread-safe if it uses multiple threads
  • Two objects that each own a shared_ptr to the other will have a cyclic ownership and they will not be deleted automatically

Weak pointer

Mechanism:

  • It is strongly related to a shared_ptr object, and it is used to keep the address memory of a shared_ptr instance without increasing the ref_count. The user can check if the pointer is still active and can ‘promote’ the weak pointer to a shared pointer in other to use it. (The weak pointer can simply observe the managed object, but they don’t keep it alive / affect its lifetime).
  • It is used to fix the cyclic reference issue (let’s remember that the shared_ptr keep the weak pointer count separately).
  • You can point a weak_ptr to an object by using the copy assignment or = operator.

There is nothing much the user can do with a weak pointer, except for checking if the pointer exists and if it’s valid.

Functions:

  • expired() : Returns true if the object is no longer valid (the shared pointer it was referring was destroyed)
    In order to use the pointer (memory pointer contained in the shared_ptr – referenced by weak_ptr), the user should create another shared_ptr based on the weak pointer it contains.

Note: The developer should always call the expired() function to assure that the object is still valid.

If I’ve missed anything or you need further help, please contact me.
Robert

https://github.com/badearobert/cplusplus

Leave a comment

Your email address will not be published.