智能指针如何在delete和delete[]之间进行选择

How do smart pointers choose between delete and delete[]?

本文关键字:delete 之间 选择 行选 指针 智能      更新时间:2023-10-16

考虑:

delete new std :: string [2];
delete [] new std :: string;

每个人都知道第一个是错误。如果第二个不是错误,我们就不需要两个不同的运算符。

现在考虑一下:

std :: unique_ptr <int> x (new int [2]);
std :: unique_ptr <int> y (new int);

x知道使用delete[]而不是delete吗?


背景:当我认为指针的数组类型限定将是一个方便的语言功能时,这个问题在我的脑海中浮现。

int *[] foo = new int [2]; // OK
int *   bar = new int;     // OK
delete [] foo;             // OK
delete bar;                // OK
foo = new int;             // Compile error
bar = new int[2];          // Compile error
delete foo;                // Compile error
delete [] bar;             // Compile error

不幸的是,他们不知道使用什么删除,因此他们使用delete。这就是为什么对于每个智能指针,我们都有一个对应的智能数组。

std::shared_ptr uses delete
std::shared_array uses delete[]

所以,你的线路

std :: unique_ptr <int> x (new int [2]);

实际上会导致未定义的行为。

顺便说一句,如果你写

std :: unique_ptr<int[]> p(new int[2]);
                     ^^

那么CCD_ 5将被使用,因为您已经明确地请求了。但是,下面的行仍然是UB。

std :: unique_ptr<int[]> p(new int);

他们不能在deletedelete[]之间进行选择的原因是new intnew int[2]完全属于同一类型——int*

这里有一个相关的问题,在smart_ptr<void>smart_ptr<Base>的情况下,当Base没有虚拟析构函数时,使用正确的deleter。

没有"神奇"的方法可以检测int*是否指:

  • 单个堆分配的整数
  • 堆分配的数组
  • 堆分配数组中的整数

类型系统丢失了信息,没有运行时方法(可移植)可以修复它。这令人愤怒,也是C++继承的C中的一个严重设计缺陷(*)(有人说是为了兼容性)。

然而,一些处理智能指针中数组的方法。

首先,您的unique_ptr类型不适合处理数组,您应该使用:

std::unique_ptr<int[]> p(new int[10]);

意味着调用CCD_ 16。我知道有人说要在Clang中实现一个特定的警告,以发现与unique_ptr的明显不匹配:这是一个实现质量问题(标准只是说它是UB),并不是所有情况都可以在没有WPA的情况下覆盖。

其次,boost::shared_ptr可以有一个自定义的deleter,如果您将其设计为调用正确的delete[]运算符,则可以。然而,有一个boost::shared_array是专门为此设计的。再一次,检测不匹配是一个实现质量问题。std::shared_ptr也遇到了同样的问题(在ildjarn的评论之后编辑)。

我同意它不好看。这似乎太令人讨厌了,以至于C起源的一个设计缺陷(*)至今仍困扰着我们。

(*)有些人会说,C非常倾向于避免开销,这会增加开销。我有点不同意:毕竟malloc总是知道块的大小。

来自微软文档:

(部分专用化unique_ptr<Type[]>管理分配有new[]的数组对象,并具有默认的删除器default_delete<Type[]>,专用于调用delete[] _Ptr。)

我加了最后两个方括号,看起来像是打字错误,因为没有它们就没有意义。

std::unique_ptr不适用于数组,因为我引用了最新的增强文档:

通常,shared_ptr不能正确地保存指向动态分配的数组。有关该用法,请参见shared_array

如果您想对指针数组进行内存管理,您可以根据自己的需求选择几个选项:

  1. 使用boost::shared_array
  2. 使用boost::shared_ptr中的std::vector
  3. 使用类似boost::ptr_vector的提升指针容器

Nicolai Josuttis⟶请注意,std::shared_ptr提供的默认deleter调用delete,而不是delete[]。这意味着只有当共享指针拥有用new创建的单个对象时,才允许使用默认deleter。不幸的是,为数组创建std::shared_ptr是可能的,但却是错误的:

std::shared_ptr<int>(new int[10]) //error but compiles

因此,如果使用new[]创建对象数组,则必须定义自己的deleter。例如

std::shared_ptr<int> ptr(new int[10], 
                         [](int* p){ delete[] p; });

std::shared_ptr<int> ptr(new int[10], 
                         std::default_delete<int[]>());

对我来说,这是最好的解决方案!