Why unique-ptr doesn#39;t check base class to virtual destructible?(为什么 unique-ptr 不检查基类是否可虚拟破坏?)
问题描述
Consider this example :
#include <cstdio>
#include <memory>
struct base
{
base( int i ): i(i) { printf("base ctor
"); }
~base() { printf("base non-virtual dtor
"); } // non-virtual
int i;
};
struct derived : public base
{
char* s;
derived(int i): base(i), s(new char[i] )
{
printf("derived ctor
");
}
~derived()
{
printf("derived dtor
");
delete [] s;
}
};
int main()
{
printf("Success
");
//raw pointer
printf("RAW-POINTER
");
{
base* b = new derived(2);
// ......
delete b; //here memory leak, but it's old- and error-prone code.
}
printf("---END-RAW-POINTER--
");
//unique-ptr
printf("UNIQUE_PTR
");
{
// 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--
");
return 0;
}
The code std::unique_ptr<base> bu( new derived(3) );
easy prohibit with std::has_virtual_destructor type-traits.
Live code
So why does above code compiled? Is this allowed by standard ?
EDIT: interesting, but with std::shared_ptr worked, i.e. both base and derived dtor will call:
printf("SHARED_PTR
");
{
std::shared_ptr<base> b(new derived(3));
}
printf("--END-SHARED_PTR--
");
Output:
SHARED_PTR
base ctor
derived ctor
derived dtor
base non-virtual dtor
--END-SHARED_PTR--
Why std::shared_ptr can call dervied class dtor, but std::unique_ptr can't ???
EDIT2: 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.
};
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));
这篇关于为什么 unique-ptr 不检查基类是否可虚拟破坏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么 unique-ptr 不检查基类是否可虚拟破坏?
基础教程推荐
- 分别使用%o和%x以八进制或十六进制格式显示整 1970-01-01
- C++输入/输出运算符重载 1970-01-01
- C++定义类对象 1970-01-01
- end() 能否成为 stl 容器的昂贵操作 2022-10-23
- C++ #define 1970-01-01
- 初始化变量和赋值运算符 1970-01-01
- 明确指定任何或所有枚举数的整数值 1970-01-01
- C语言访问数组元素 1970-01-01
- C++按值调用 1970-01-01
- 使用scanf()读取字符串 1970-01-01