This is the third post of several posts I wrote related to smart pointers:
- Smart pointers
- unique_ptr
- More on unique_ptr
- shared_ptr
- 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
;) ).
Nice, especially with fclose(). I always forgot that templates can also take function signature;)
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.
Thanks! You are completely right, I will fix it according your suggestions :)