Is there a convenient way to get unique_ptr to automatically use a deleter like shared_ptr?

std::shared_ptr has an excellent template constructor that automatically creates the correct deleter for this type (constructor No. 2 in this link).

So far, I (erroneously) thought that std::unique_ptr has a similar constructor, but when I ran the following code:

 #include <memory> #include <iostream> // Notice nothing is virtual struct Foo { ~Foo() { std::cout << "Foo\n"; } }; struct Bar : public Foo { ~Bar() { std::cout << "Bar\n"; } }; int main() { { std::cout << "shared_ptr:\n"; std::shared_ptr<Foo> p(new Bar()); // prints Bar Foo } { std::cout << "unique_ptr:\n"; std::unique_ptr<Foo> p(new Bar()); // prints Foo } } 

I was surprised to learn that unique_ptr does not call the Bar destructor.

What is a clean, simple, and correct way to create a unique_ptr that has the correct de-ester for a given pointer? Especially if I want to save the entire list of these (i.e. std::vector<std::unique_ptr<Foo>> ), which means that they should all have a heterogeneous type?

(goodbye poor title, feel free to offer the best)

+5
source share
2 answers

Here is one way:

 { std::cout << "unique_ptr<Bar, void(void*)>:\n"; std::unique_ptr<Foo, void(*)(void*)> p( new Bar(), [](void*p) -> void { delete static_cast<Bar*>( p ); } ); // prints Bar Foo } 

The main problem with this approach is that unique_ptr supports conversion to a logical "pointer to the base class", but the standard does not guarantee that conversion to void* will then give the same address. In practice, this is only a problem if the base class is not polymorphic and the derived class is polymorphic by introducing vtable ptr and possibly changing the memory layout a bit. But in this possible, but not entirely probable situation, dropping back into a deleter would lead to an incorrect pointer value and a hit.

Thus, the foregoing is not formally safe with respect to such transformations.


To do roughly the same thing that shared_ptr does ( shared_ptr supports conversion to a logical pointer to the base), you will also need to save the original void* pointer along with the deletion.


In general, when you manage the top-most base class, make its destructor virtual.

It takes care of everything.

+5
source

You must make a Foo virtual destructor. This is good practice, whether you unique_ptr or not. It will also take care of the problem you are dealing with.

+7
source

Source: https://habr.com/ru/post/1204036/


All Articles