【问题标题】:Why can not I use `new [ ]` with smart_pointers?为什么我不能将 `new [ ]` 与 smart_pointers 一起使用?
【发布时间】:2015-11-08 13:02:11
【问题描述】:

为什么我不能将new [ ] 与 smart_pointers 一起使用?

其实这段文字我看不懂。

警告您应该仅将 auto_prt 或 shared_ptr 对象用于 new 分配的内存,而不是 new [] 分配的内存。你 不应该使用 auto_ptr、shared_ptr、orunique_ptr 作为内存 通过 new 分配,或者在 unique_ptr 的情况下,通过 new 或 new[] 分配。

【问题讨论】:

  • 实际上,您根本不应该使用auto_ptr,它已被弃用,并将在下一个标准中删除。此外,std::unique_ptr 使用new[] 对数组进行了专门化,有关如何使用它的示例,请参见this operator[] reference。
  • 举个例子会更容易理解怎么做和不做
  • 我不确定shared_ptr(不查找它,很少使用它),但unique_ptr最明确专门用于数组,并且工作正常(定义明确)。

标签: c++11 stl smart-pointers


【解决方案1】:

示例:

#include <memory>

int
main()
{
    auto p1 = std::unique_ptr<char[]>{new char[3]};  // C++11
    auto p2 = std::shared_ptr<char>{new char[3], [](char* p) {delete [] p;}}; // C++11
    auto p3 = std::make_unique<char[]>(3);           // C++14
}

第一个和第二个适用于 C++11 及更高版本。第 3 个是在 C++14 中引入的。第一个和第三个代表新的唯一所有权,第二个代表新的共享所有权。

【讨论】:

    【解决方案2】:

    为什么我不能将new[] 与智能指针一起使用?

    通常可以,但是智能指针必须知道它存储的是动态分配的数组,而不是单个对象。这是因为使用运算符 new[] 分配的对象应该使用运算符 delete[] 释放,不是 delete。智能指针如何知道应该应用哪个运算符?

    区别在于为数组类型提供了智能指针类模板的特化,就像目前在std::unique_ptr&lt;T&gt;中所做的那样:

    std::unique_ptr<int> ptr(new int);      // will call delete
    std::unique_ptr<int[]> arr(new int[5]); // will call delete[]
                       ↑↑
    

    DEMO

    但是,该语法并不(还)适用于标准库中可用的所有智能指针类型。

    为了比较,Boost Smart Pointers 库提供了单独的类模板来存储指向动态分配数组的指针:

    boost::shared_array<int> arr1(new int[5]); // will call delete[]
    //            ~~~~^
    boost::scoped_array<int> arr2(new int[5]); // will call delete[]
    //            ~~~~^
    

    DEMO 2


    你应该只为new分配的内存使用auto_ptr或shared_ptr对象,而不是new []分配的内存。

    std::auto_ptr&lt;T&gt;(† 2017)1 将普通的 delete 运算符应用于它存储的指针,并且无法更改该行为。因此,存储指向数组的指针不是一种选择。

    就std::shared_ptr&lt;T&gt; 而言,默认情况下它的作用相同(调用运算符delete)。要改变这种行为,并正确释放存储数组的内存区域,您可以使用自定义删除器,例如 std::default_delete&lt;T[]&gt;:

    std::shared_ptr<int> arr(new int[5], std::default_delete<int[]>{});
                                                                ↑↑
    

    或其他一些您自己提供的:

    std::shared_ptr<int> arr(new int[5], [](int* ptr) { delete[] ptr; } );
    //                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
    

    DEMO 3

    但是,缺少 std::shared_ptr&lt;T[]&gt; 的特化意味着没有 operator[] 可以让您轻松访问存储数组的元素,这会导致语法不直观,例如 arr.get()[0]。

    随着提案N4077 的引入,将专门针对数组类型指针:

    std::shared_ptr<int[]> arr(new int[5]); // will call delete[]
                       ↑↑
    

    您不应该使用auto_ptr、shared_ptr 或unique_ptr 来表示不是通过new 分配的内存,或者在unique_ptr 的情况下通过new 或new[] 分配的内存。

    这段摘录只是说明不应从指向未动态分配的对象的指针构造智能指针,因为(默认情况下)它会导致在未分配 new 的对象上调用 delete (同上new[]/delete[])。


    unique_ptr&lt;double[]&gt; p1(new double[2]);、unique_ptr&lt;double&gt; p2(new double[2]);、unique_ptr&lt;double[]&gt; p3(new double(2)); 有什么区别?

    1. std::unique_ptr&lt;double[]&gt; p1(new double[2]);

    OK:从两个doubles 的数组 的指针构造一个unique_ptr(并获得所有权)。它将调用delete[] 来释放指向的内存。

    1. std::unique_ptr&lt;double&gt; p2(new double[2]);

    错误:从一个指向两个doubles 的数组 的指针构造一个unique_ptr(并获得所有权)。它将调用delete (!) 来释放指向的内存。 (可能是未定义的行为 - new[] 和 delete 不匹配)。

    1. std::unique_ptr&lt;double[]&gt; p3(new double(2));

    错误:从一个指针构造一个unique_ptr(并取得其所有权),该指针指向一个初始化为值2的单个double。它将调用delete[] (!) 来释放指向的内存。 (可能是未定义的行为 - new 和 delete[] 不匹配)。


    1std::auto_ptr&lt;T&gt; 自 C++11 起被视为已弃用,取而代之的是 std::unique_ptr&lt;T&gt;,并将根据 N4168 从 C++1z 中的标准库中删除。

    【讨论】:

    • @Piotr Skotnicki 谢谢!
    猜你喜欢
    • 2020-08-26
    • 2013-07-02
    • 2013-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 2011-06-05
    相关资源
    最近更新 更多