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 :)