为什么unique-ptr不会将基类检查为虚拟可破坏的? [英] Why unique-ptr doesn't check base class to virtual destructible?

查看:117
本文介绍了为什么unique-ptr不会将基类检查为虚拟可破坏的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下示例:

#include <cstdio>
#include <memory>

struct base
{
    base( int i ): i(i)    {    printf("base ctor\n"); }
    ~base() {     printf("base non-virtual dtor\n"); } // non-virtual
    int i;
};

struct derived : public base
{
    char* s;
    derived(int i): base(i), s(new char[i] )
    {
        printf("derived ctor\n");
    }
    ~derived()
    {
        printf("derived dtor\n");
        delete [] s;
    }
};

int main()
{
    printf("Success\n");

    //raw pointer
    printf("RAW-POINTER\n");
    {
        base* b = new derived(2);
         // ......
        delete b; //here memory leak, but it's old- and error-prone code.
    }
    printf("---END-RAW-POINTER--\n");

    //unique-ptr
    printf("UNIQUE_PTR\n");
    {
       // I would that, this doesn't compile, because base- has not virtual destructor.
        std::unique_ptr<base> bu( new derived(3) ); // here still memory leak !!!!
    }
    printf("--END-UNIQUE_PTR--\n");


    return 0;
}

使用 std :: has_virtual_destructor 类型特征可以轻松禁止代码std::unique_ptr<base> bu( new derived(3) );. 实时代码

The code std::unique_ptr<base> bu( new derived(3) ); easy prohibit with std::has_virtual_destructor type-traits. Live code

那为什么要编译以上代码?这是标准允许的吗?

有趣,但是在std :: shared_ptr起作用的情况下,即基本dtor和派生dtor都将调用:

interesting, but with std::shared_ptr worked, i.e. both base and derived dtor will call:

    printf("SHARED_PTR\n");
    {
        std::shared_ptr<base> b(new derived(3));
    }
    printf("--END-SHARED_PTR--\n");

Output:
SHARED_PTR
base ctor
derived ctor
derived dtor
base non-virtual dtor
--END-SHARED_PTR--

为什么std :: shared_ptr可以调用派生类dtor,但std :: unique_ptr不能????

很简单,我需要类似的东西:

simple I need something like:

template< typename T, typename D = default_deleter<T> >
class unique_ptr{
  .............

  template< typename U >
  unique_ptr( U* u ) if ( U != T && T - is class && T is base of U, and D - is default_deleter, and T - has not virtual destructor ) then = delete this ctor.

};

推荐答案

unique_ptrshared_ptr之间的区别在于标准的语言,涉及其析构函数(和构造函数).适用于您的示例的用于两个智能指针的删除器的语言相似但细微不同:

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.

您可以看到,在两种情况下,标准都要求调用删除程序,但决定删除程序的方式有所不同,并且unique_ptr删除了通过get()获得的 pointer ,而shared_ptr删除对象.这种区别很重要.看看这两个类的构造函数也有何不同:

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:

shared_ptr的定义如下:

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

unique_ptr显式单参数构造函数为

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;
...

观察到unique_ptr只是获取该类型的默认删除(在您的情况下为普通的delete),并存储指针.但是,shared_ptr<T>构造函数未在T(!)上进行模板化,而是根据与其一起构造的对象Y的类型进行模板化.因此,在您的情况下,

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));

shared_ptr将使用T=baseY=derived构造,从而允许显式销毁派生对象,并且在示例中不会泄漏内存.

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.

虽然不能更改标准,但是可以做的是从项目中的unique_ptr继承,或者提供自己的包装器以实施所需的行为.例如,

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));

这篇关于为什么unique-ptr不会将基类检查为虚拟可破坏的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆