×
Get in touch

Get in touch with Binary

Binary Studio website uses cookies to enhance your browsing experience. By continuing to use our site, you agree to our Privacy Policy and use of cookies.

Learn more more arrow
Agree
andrew.kramarenko@binary-studio.com'
Andrew Kramarenko C++ Developer 04.07.2014

4. Haters gonna hate or life without pointers in C++11 times


Introduction

In my previous article I have described how do not use pointers. Before C++11 times it wasn't so easy task, but it's possible. With coming C++11 standard life of programmer became better. Heretofore programmer should use third-party library to use RAII-like entities which are smarter than std::auto_ptr<T> in managing memory. Due to the fact that std::auto_ptr<T> doesn't have reference counting and doesn't provide good sharing data between entities in code.

Now C++ standard library has such cool stuff as: std::unique_ptr<T>, std::shared_ptr<T>, std::weak_ptr<T>. Also I am going to talk about std::array<T> little bit, because it is more convenient instead of static arrays.

Enumerated entities suit for different cases and give a programmer good tools for resolving  issues.

std::unique_ptr<T> instead of std::auto_ptr<T>

or saver life with unique variable

This smart pointer was born as replacement for std::auto_ptr<T>. That's why the last one is deprecated in this standard. std::unique_ptr<T> has similar interface as std::auto_ptr<T>, but it's better designed and implemented what allows to use this smart pointer when std::auto_ptr<T> can't be used. Let's talk about everything in turn.

std::unique_ptr<T> has same user cases as std::auto_ptr<T>. Remind you:

  • wrap class field which allocated on heap;
  • wrap local variable which allocated on heap;
  • wrap input argument which allocated on heap;
  • wrap returned value on heap.

From name of smart pointer you should have guessed that only single one std::unique_ptr<T> holds managed instance and it can't be transferred through assignment operator or copy constructor because it is forbidden. But it can be done by move semantics of C++11. See:

This works with std::move() because operator =(std::unique_ptr<T>&) is forbidden, but operator =(std::unique_ptr<T>&&) is not. std::unique_ptr<T>&& is a reference to r-value of std::unique_ptr<T> instance. More details about such references to r-value you can read this. It is very important topic about C++11, but is not directly related to this article.

Unlike std::auto_ptr<T> unique smart pointer can be used for arrays, because its template declaration has specification for arrays. It correctly deallocates memory, which is allocated with operator new []. Check it out:

So instead of using std::vector<T> like I wrote in previous article in C++11 you can use std::unique_ptr<T[]> for holding dynamic arrays in cases if std::vector<T> doesn't fit your needs. If you want, you can get pointer to allocated memory with help of method std::unique_ptr<T>::get(). But don't forget – when unique smart pointer goes out of scope, it frees memory.

std::shared_ptr<T> helps to share instance with friends or finally we have standard reference counting

std::unique_ptr<T> does not allow sharing instance. Same as std::auto_ptr<T> it doesn't have reference counting. For such target you have to use std::shared_ptr<T>. It counts how many shared pointers hold same instance and destroy instance only when the last referenced pointer is destroyed or it is reset with new value through std::shared_ptr<T>::reset(T*).

Typical implementation of shared pointer has fields with managed instance and control block. Control block holds different information about allocation and deallocation of instance and number of smart pointers which point to the same managed instance.

Besides these control block holds pointer to managed instance or this instance itself, in case of using std::make_shared which results in object and control block sharing the same memory block. See the following code:

std::shared_ptr<Resource> extraAllocation(new Resource());   std::shared_ptr<Resource> singleAllocation = std::make_shared<Resource>();

In the first case instance and control block are allocated separately. In such way you have two allocations and control block holds pointer to created instance. By using std::make_shared<T>() it is done at one time. It creates managed instance and control block at single allocation. It means – it takes less time and has exception safety. Because of that second way is more preferable. In C++14 there will be similar method for unique smart pointer called std::make_unique<T>(). It is added just for convenience. It doesn't have double allocation, but it is awesome to use such help functions.

As I said managed instance of shared smart pointer dies when last such pointer to instance is gone away. Lets see the following code for proving which uses std::shared_ptr<T>::use_count() method to show how many pointers manage same instance:

As you see std::shared_ptr<T> keeps instance alive while some smart pointer still points to it. So main use case for std::shared_ptr<T> is sharing some object in different part of program and keeping it alive. By the way std::shared_ptr<T> is thread safe type. So it can be used for sharing data in multi-threaded applications, but don't overuse it, because it is significant performance penalty.

std::weak_ptr<T> could be dead

or never mind when it is alive

If std::smart_ptr<T> cares about keeping instance alive, std::weak_ptr<T> works in opposite way. This smart pointer doesn't uphold lifetime of addressed instance. Because of this weak smart pointer is used for cases when you need access to instance when it is still alive. This type of smart pointer is constructed from std::shared_ptr<T>. Take a look at next code:

As you see std::weak_ptr<T> is constructed from std::shared_ptr<T>. In my example I did it with assignment operator, but it can be done by passing shared pointer to constructor of weak pointer as well.

When you are going to use managed instance from weak pointer you have to call method std::weak_ptr<T>::lock(). It returns std::shared_ptr<T> which extends lifetime of an instance while you are using it. If instance is already destroyed, returned shared pointer will be empty. Also you can use method std::weak_ptr<T>::expired() which indicates whether managed instance still presents or you can forget about its existence.

Underlining that was said std::weak_ptr<T> uses for delivering or holding managed instance somewhere for using it without worrying whether it is alive or not. Also its implementation and usage are based on thread safe smart shared pointer, so std::weak_ptr<T> has same ability in multi-threaded applications.

std::array<T> as cool as static arrays

or avoid using static C-style arrays

Of course you can use std::unique_ptr<T[]> for arrays as I have shown above. But C++11 also provides simpler and quicker container for static arrays, which not worse than C-style arrays. I am talking about std::array<T, N>. This container provides storing of fixed range of instances and different methods for enumerating them with iterators.

The following code shows how such array can be used with std::sort() function:

As you probably noticed std::array<T, N> has two template arguments. T is type of stored instances and N is number of such instances. In shown example I have defined static array with integer values which size is 3. Through std::sort() I have sorted values of this static array. I have used std::begin() and std::end() because it is more preferably in C++11, because implementation of such methods encapsulate method of obtaining first or last iterators of containers.

There is std::dynarray<T> in proposal for C++14 standard. It allows to handle dynamic arrays in same way. So it can be totally used instead of std::vector<T> in such cases. It is simpler than std::vector<T> in using. Unfortunately, it looks like it won't make it in the new standard, but you are aware of this now. Someday it will come as <dynarray> include header.

Conclusion

In this article I have told about C++11 stuff which can be used to avoid using raw pointers. C++11 gives a lot of smart pointers for different goals, such as: std::unique_ptr<T>, std::unique_ptr<T[]>, std::shared_ptr<T> and std::weak_ptr<T>. std::unique_ptr<T> is used for owning single instance and handling its lifetime. std::unique_ptr<T[]> has similar means, but for set of instances. std::shared_ptr<T> is used for managing instance between different part of application and extends lifetime of owned instance. std::weak_ptr<T> is used when you want to work with instance without owning it. Also as separate entity I noticed std::array<T, N>, which can be used instead of static C-style arrays.

Here you can find links to previous articles of this series:

* “Do not muddle pointers and references”

* “Extended talk about references and pointers”

* “Haters gonna hate or life without pointers before C++11”

As usual here is Github repository with code of those articles.