为什么unique-ptr不会将基类检查为虚拟可破坏的? [英] Why unique-ptr doesn't check base class to virtual destructible?
问题描述
考虑以下示例:
#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_ptr
和shared_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=base
但Y=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屋!