Jonathan Boccara's blog

Changing deleters during the life of a unique_ptr (4/7)

Published September 5, 2017 - 2 Comments

Daily C++

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:

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:

unique_ptr reset deleter

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:

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed