Changing deleters during the life of a unique_ptr (4/7)
A previous episode in the Smart developers use Smart pointers series showed how (and why) to use custom deleters in std::unique_ptr
. Now let’s see the methods that change the custom deleter during the life of the unique_ptr and, also, those that don’t. This aspect of smart pointers has been pointed out to me by Mathieu Ropert and Raoul Borges. Thanks guys.
The series Smart developers use Smart pointers currently contains:
- Smart pointer basics
- unique_ptr, shared_ptr, weak_ptr, scoped_ptr, raw pointers: clearly stating your intentions by knowing your smart pointers
- Custom deleters and How to make them more expressive
- Changing deleters during the life of a unique_ptr
- How to implement the pimpl idiom by using unique_ptr
- How to make a polymorphic clone in modern C++
- How to Return a Smart Pointer AND Use Covariance (by Raoul Borges)
Throughout the following cases, we will use a unique_ptr on a type that can be destroyed in two different ways. To see why this can be useful, check out the post dedicated to Custom deleters.
As a toy example we use an unique_ptr on int
, with a customizable deleter:
using IntDeleter = void(*)(int*); using IntUniquePtr = std::unique_ptr<int, IntDeleter>;
One deleter is to be used for even numbers, and another one for odd numbers:
void deleteEvenNumber(int* pi) { std::cout << "Delete even number " << *pi << '\n'; delete pi; } void deleteOddNumber(int* pi) { std::cout << "Delete odd number " << *pi << '\n'; delete pi; }
Assigning from another std::unique_ptr
Consider the following code:
IntUniquePtr p1(new int(42), deleteEvenNumber); IntUniquePtr p2(new int(43), deleteOddNumber); p1 = move(p2);
p1
, that contains an even number with the appropriate deleter, is taking over the ownership of the resource in p2
. The question is: how will it destroy this resource? Will it use the deleter it was built with, or rather bring over the deleter of p2
along with the ownersihp of its resource?
Take a moment to think about it, then click below to uncover what this program outputs (the deleters are printing out the info – look at their code at the top of the article):
Delete even number 42 Delete odd number 43
Each resource is deleted with the correct deleter, which means that the assignment did bring over the deleter. This makes sense because the resources would not be disposed of with the correct deleter otherwise.
Resetting the pointer
Another way to change the resource contained in an std::unique_ptr
is to call its reset
method, like in the following simple example:
std::unique_ptr<int> p1(new int(42)); p1.reset(new int(43));
The reset
method calls the deleter on the current resource (42), and then takes on the new one (43).
But the reset
method only takes one argument, which is the new resource. It cannot be passed a deleter along with this new resource. For that reason, it can no longer be used directly in our example with even and odd numbers. Indeed, the following code:
IntUniquePtr p1(new int(42), deleteEvenNumber); p1.reset(new int(43)); // can't pass deleteOddNumber
naturally outputs:
Delete even number 42 Delete even number 43
which is incorrect in our case.
In fact we could manually change the deleter in a separate statement, by exploiting the fact that the get_deleter
method of unique_ptr
returns the deleter by non-const reference (thanks to Marco Arena for pointing this out):
p1.get_deleter() = deleteOddNumber;
But why doesn’t reset
have a deleter argument? And how to hand over a new resource to an std::unique_ptr
along with its appropriate deleter in a single statement?
Howard Hinnant, who is amongst many other things lead designer and author of the std::unique_ptr
component, answers this question on Stack Overflow:
And here is how to use his answer in our initial example:
IntUniquePtr p1(new int(42), deleteEvenNumber); p1 = IntUniquePtr(new int(43), deleteOddNumber);
which gives the following desired output:
Delete even number 42 Delete odd number 43
Thanks to Howard for providing that solution.
Stay tuned for the next episode of the series Smart Developers Use Smart Pointers!
Related articles:
- Smart pointer basics
- unique_ptr, shared_ptr, weak_ptr, scoped_ptr, raw pointers: clearly stating your intentions by knowing your smart pointers
- Custom deleters and How to make them more expressive
- How to implement the pimpl idiom by using unique_ptr
- How to make a polymorphic clone in modern C++
- How to Return a Smart Pointer AND Use Covariance (by Raoul Borges)
Share this post!