C++: Smart pointers, part 3: More on unique_ptr

This is the third post of several posts I wrote related to smart pointers:

  1. Smart pointers
  2. unique_ptr
  3. More on unique_ptr
  4. shared_ptr
  5. weak_ptr

Ok, here I am going to write about two other features that unique_ptr has that I did not mention in my last post.

unique_ptr default behavior consists on take ownership of a pointer created with new and that would normally be released with delete.

That is because the main unique_ptr declaration is:

template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr;

The template class default_delete implements a function object that performs a delete to the object pointed by the argument passed to the function object. Something like this:

template <typename T>
class default_delete
{
public:
    void operator()(T* obj) const
    {
        delete obj;
    }
};

The idea of having the “deleter” as a template argument, lets us to use a unique_ptr to hold a pointer to a data structure created with, say, malloc or to free the memory using some custom deallocator.

Let’s see both cases:

1. Using with malloc

Let’s create a struct that holds two integers:

struct my_point
{
    int x; 
    int y;
};

I want to create one instance of such struct in the heap using malloc, to have something like this:


void show_point(const my_point& o)
{
  cout << "(" << o.x << "; " << o.y << endl;
}

void function_that_possibly_throws_exception()
{
   .....
   .....
}

int main()
{
  my_point* p = static_cast<my_point*>(malloc(sizeof(my_point));
  p->x = 5;
  p->y = 8;
  show_point(*p);
  function_that_possibly_throws_exception();
  free(p);
}

If I want to use unique_ptr to make sure that free is always invoked no matter if an exception occurs or not or if my function contains several return points; I need to implement a “custom deleter” to replace the std::default_delete deleter; something like this:

struct my_point_deleter
{
  void operator()(my_point* p) const
  {
    free(p);
  }
};

Take into account that my implementation is very similar to the default_delete implementation but uses free instead of delete.

To use it into my code, I need to modify my main function to be similar to this one:

int main()
{
  unique_ptr<my_point, my_point_deleter> p(static_cast<my_point*>(malloc(sizeof(my_point)));
  p->x = 5;
  p->y = 8;
  show_point(*p);
  function_that_possibly_throws_exception();
  //free(p); --> no need for free, the unique_ptr will take care of this
}

2. Using with a custom deallocator

For example, I want to use unique_ptr to invoke fclose() everytime a FILE* gets out of scope.

Look at my implementation, as you see below, you can pass the custom deleter as a function pointer in the constructor; so I can provide that to my unique_ptr also as a lambda expression:

int main()
{
  unique_ptr<FILE, void (*)(FILE*)> f(fopen("file.cpp", "r"), 
                                      [](FILE* f)
                                      {
                                        fclose(f);
                                      });

  char aux[100];
  while (!feof(f.get())) //f.get returns me the actual plain old pointer
  {
     fgets(aux, 100, f.get());
     cout << aux;
  }

  //No need to invoke fclose() because the unique_ptr will take care of it.
}

C++14 will ship with a new variadic template function called make_unique that will make easier to create objects in the heap and wrap them into unique_ptr instances.

So, consider this instantiation using new:

unique_ptr<my_point> point(new my_point { 6, 5 });

can be rewritten to this one, using make_unique:

auto point = make_unique<my_point>(6, 5);

Pros? As Stephan T. Lavavej states, it saves to write “my_point” twice, it is consistent with make_shared (I will write about it in a later post), and it hides the usage of the operator new (that is good because we do not want to have our programs with new but without delete ;) ).

Advertisement

3 thoughts on “C++: Smart pointers, part 3: More on unique_ptr

  1. Nice, especially with fclose(). I always forgot that templates can also take function signature;)

  2. Your while loop in “2. using custom deallocator” is not correct i.e. `while (!feof(f.get()) { … }` should be `while (fgets(aux,sizeof(aux),f.get() !=NULL) {…}` otherwise it loops one extra time repeating the last line, anyway thanks for a good post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s