C ++ Rule of Zero: polymorphic deletion and unique_ptr behavior

3 answers

You have your answer here: fooobar.com/questions/182192 / ...

Quote:

As soon as the last referenced shared_ptr is out of scope or reset, ~Derived() will be called and memory will be released. Therefore you do not need to do ~Base() virtual. unique_ptr<Base> and make_unique<Derived> do not provide this function because they do not provide the shared_ptr mechanics regarding the deleter, since the unique pointer is much simpler and targets the smallest overhead and therefore does not store the extra function pointer needed for the deleter.

+3
source

It will work if you use C ++ 14 make_unique or write your own, as in Yakka's answer. Basically the difference between common pointer behavior is what you got:

 template< class T, class Deleter = std::default_delete<T> > class unique_ptr; 

for unique_pointer , and as you can see, the deleter belongs to the type. If you declare unique_pointer<Base> , it will always use std::default_delete<Base> by default. But make_unique will take care of using the correct deleter for your class.

When using shared_ptr you got:

 template< class Y, class Deleter > shared_ptr( Y* ptr, Deleter d ); 

and other overloads as a constructor. Since you can see that the default default for unique_ptr depends on the template parameter when declaring the type (unless you use make_unique ), whereas for shared_ptr deleter depends on the type passed to the constructor.


You can see the version that allows polymorphic deletion without a virtual destructor here (this version should also work in VS2012). Note that this is a bit hacky, and I'm currently not sure what the unique_ptr and make_shared behavior will look like in C ++ 14, but I hope they make it easier. Maybe I’ll look at the docs for the C ++ 14 add-ons and see if something has changed if I get the time later.

+2
source
 template<typename T> using smart_unique_ptr=std::unique_ptr<T,void(*)(void*)>; template<class T, class...Args> smart_unique_ptr<T> make_smart_unique(Args&&...args) { return {new T(std::forward<Args>(args)...), [](void*t){delete (T*)t;}}; } 

The problem is that the default release for unique_ptr causes delete on the saved pointer. The above is the deletir keeper, which knows the type when building, so when copying to the base class, unique_ptr will still be deleted as a child.

This adds a modest overhead since we have to dereference a pointer. In addition, it denormalizes the type, since smart_unique_ptr built by default smart_unique_ptr now illegal. You can fix this with some extra work (replace the raw function pointer with a semi-small functor, which at least won't work: the function pointer, however, must assert that exists if unique not empty when the divider is called).

+1
source

All Articles