Why unique-ptr doesn't check base class to virtual destructible?

后端 未结 2 792
日久生厌
日久生厌 2020-12-13 20:51

Consider this example :

#include 
#include 

struct base
{
    base( int i ): i(i)    {    printf(\"base ctor\\n\"); }
    ~base(         


        
相关标签:
2条回答
  • 2020-12-13 21:18

    Not all unique_pointers are used polymorphically:

    std::unique_ptr<int> p(new int(42));
    

    This would not compile with the restriction you propose. Same with classes:

    std::unique_ptr<YourClassHere> p(new YourClassHere);
    
    0 讨论(0)
  • 2020-12-13 21:30

    The difference between unique_ptr and shared_ptr is in the language of the standard, with respect to their destructors (and constructors). That language for the deleters of both smart pointers, which applies to your example, is similar but subtly different:

    [20.7.1.2.2] unique_ptr destructor 
    ... If get() ==  nullptr there are no efects. Otherwise get_deleter()(get()).
    
    [20.7.2.2.2] shared_ptr destructor
    ... if *this owns an object p and a deleter d, d(p) is called.
    

    You can see that in both cases the standard says to call the deleter, the difference though in how the deleter is decided on, and that unique_ptr deletes the pointer it obtains via get(), while shared_ptr deletes the object. This distinction is important. Look at how the constructors for both classes are also different:

    The shared_ptr is defined as follows:

    template <class  T> 
    class  shared_ptr  {
    ...
        template<class  Y>  explicit  shared_ptr(Y*  p);
    

    While the unique_ptr explicit single argument constructor is,

    template <class  T, class D = default_delete<T>>
    class  unique_ptr  {
    ...
        explicit  unique_ptr(pointer  p)  noexcept;
    ...
    

    Observe that unique_ptr just gets the default delete for the type, that would be the plain delete in your case, and stores the pointer. However, shared_ptr<T> constructor is not templated on T (!), it is templated on the type of the object Y that it is constructed with. Thus, in your scenario,

    std::shared_ptr<base> b(new derived(3));
    

    the shared_ptr will be constructed with T=base but Y=derived, allowing to explicitly destroy the derived object, and not leaking memory in your example.


    While you cannot change the standard, what you can do is either inherit from unique_ptr in your project, or provide your own wrappers to enforce the desired behaviour. For example,

    namespace {
        template <class  T>
        struct checked_delete : public std::default_delete<T> {
            static_assert(std::has_virtual_destructor<T>::value, "");
        };    
    
        template <class T, class D = checked_delete<T>, class U>  
        std::unique_ptr<T, D>
        make_unique_ptr(U* p) { return std::unique_ptr<T, D>(p, D()); }    
    }
    
    // now this won't compile, because checked_delete<base> will not compile:
    auto bu = make_unique_ptr<base>(new derived(3));
    
    0 讨论(0)
提交回复
热议问题