为什么unique-ptr不检查基类是否可以虚拟破坏?

Khu*_*hid 15 c++ polymorphism unique-ptr c++11

考虑这个例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

代码std::unique_ptr<base> bu( new derived(3) );很容易禁止使用std :: has_virtual_destructor类型特征. 实时代码

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

编辑:有趣,但是std :: shared_ptr工作,即base和derived dtor都会调用:

    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--
Run Code Online (Sandbox Code Playgroud)

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

EDIT2:简单我需要这样的东西:

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.

};
Run Code Online (Sandbox Code Playgroud)

moc*_*ace 14

之间的差unique_ptrshared_ptr在标准的语言,相对于它们的析构函数(和构造函数).适用于您的示例的两个智能指针的删除者的语言类似但略有不同:

[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(),同时shared_ptr删除对象.这种区别很重要.看看这两个类的构造函数是如何不同的:

shared_ptr定义如下:

template <class  T> 
class  shared_ptr  {
...
    template<class  Y>  explicit  shared_ptr(Y*  p);
Run Code Online (Sandbox Code Playgroud)

虽然unique_ptr明确的单参数的构造函数是,

template <class  T, class D = default_delete<T>>
class  unique_ptr  {
...
    explicit  unique_ptr(pointer  p)  noexcept;
...
Run Code Online (Sandbox Code Playgroud)

观察到unique_ptr只获取类型的默认删除,delete在您的情况下将是plain ,并存储指针.但是,shared_ptr<T>构造函数不在T(!)上进行模板化Y,它是根据构造它的对象的类型进行模板化的.因此,在您的方案中,

std::shared_ptr<base> b(new derived(3));
Run Code Online (Sandbox Code Playgroud)

shared_ptr将与构造T=base,但是Y=derived,允许明确地销毁派生的对象,并在你的例子没有内存泄漏.


虽然您无法更改标准,但您可以做的是从unique_ptr项目中继承,或者提供自己的包装来强制执行所需的行为.例如,

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));
Run Code Online (Sandbox Code Playgroud)


fre*_*low 5

并非所有 unique_pointers 都以多态方式使用:

std::unique_ptr<int> p(new int(42));
Run Code Online (Sandbox Code Playgroud)

这不会符合您提出的限制。与类相同:

std::unique_ptr<YourClassHere> p(new YourClassHere);
Run Code Online (Sandbox Code Playgroud)