【问题标题】:Double (two-dimensional) array using std::unique_ptr使用 std::unique_ptr 的双(二维)数组
【发布时间】:2012-04-05 08:39:30
【问题描述】:

我有一个由指向指针的指针分配的双数组。

  // pointer to pointer
  int **x = new int *[5];   // allocation
  for (i=0; i<5; i++){
      x[i] = new int[2];
  }

  for (i=0; i<5; i++){      // assignment
      for (j=0; j<2; j++){
          x[i][j] = i+j;
      }
  }

  for (i=0; i<5; i++)   // deallocation
      delete x[i];
  delete x;

我正在尝试使用unique_ptr

std::unique_ptr<std::unique_ptr<int>[]> a(new std::unique_ptr<int>[5]);
  for (i=0; i<5; i++)
      a[i] = new int[2];

但一直收到错误消息说no operator = matches these operands。我在这里做错了什么?

【问题讨论】:

    标签: c++ c++11 unique-ptr


    【解决方案1】:
    #include <iostream>
    #include <memory>
    
    #define print(x) std::cout << x
    #define println(x) std::cout << x << std::endl
    
    int main() {
        std::unique_ptr<std::unique_ptr<int[]>[]> arr(new std::unique_ptr<int[]>[2]());
        for (int i = 0; i < 2; i++)
        {
            arr[i] = std::make_unique<int[]>(5);
            for (int j = 0; j < 5; j++) {
                arr[i][j] = j;
                println(arr[i][j]);
            }
            println(arr[i]);
        }
    }
    

    【讨论】:

    • "#define println(x) std::cout
    • @Blastfurnace 和其他人,我问了同样的问题,他们给了我这个解决方案并且它有效,所以我认为它会有所帮助:)
    • @Blastfurnace 我已经编辑了代码,现在好吗?
    【解决方案2】:

    您的代码正在有效地操作 int 数组。

    在 C++ 中,您通常希望将其实现为:

    std::vector<std::vector<int> > x;
    

    这对于unique_ptr 来说不是一个好案例。此外,您不需要使用指向unique_ptr 的指针并动态分配unique_ptr 对象。 unique_ptr 的重点是消除指针的使用并提供对象的自动分配和释放。

    【讨论】:

    • 感谢您的意见。我理解 unique_ptr 的方式是它确保它指向的实例只有 1 个引用。因此,使用 unique_ptr 指向 unique_ptr 来创建矩阵应该可以使用 unique_ptr,因为不会有另一个对实例的引用。另外,我不明白最后一句话背后的原因。谢谢。
    • 任何 RAII 类别都应提供相同的独特保证。大多数 C++ 类都是 RAII。所以你应该使用正确的工具来完成这项工作。在这里,vector 和 array 应该优先于 unique_ptr。
    • 唯一ptrs的原因主要是为了保存动态分配的单个对象。我无法立即想到将数组存储在 unique_ptr 中的理由。
    • 它有一些很好的用例(当然!),因为它是在标准中专门满足的:主要模板unique_ptr&lt; T &gt; 和专业化unique_ptr&lt; T[] &gt;。我猜这不仅仅是为了完整性,因为shared_ptr 没有这样的专业化。
    【解决方案3】:

    您不能将int* 分配给std::unique_ptr&lt;int[]&gt;,这是导致错误的原因。正确的代码是

          a[i] = std::unique_ptr<int[]>(new int[2]);
    

    但是,piokuc 是正确的,将 unique_ptr 用于数组是非常不寻常的,因为这就是 std::vectorstd::array 的用途,具体取决于是否提前知道大小。

    //make a 5x2 dynamic jagged array, 100% resizable any time
    std::vector<std::vector<int>> container1(5, std::vector<int>(2)); 
    //make a 5x2 dynamic rectangular array, can resize the 5 but not the 2
    std::vector<std::array<int, 2>> container1(5); 
    //make a 5x2 automatic array, can't resize the 2 or 5 but is _really fast_.
    std::array<std::array<int, 2>, 5> container;
    

    所有这些都可以像您已经拥有的代码一样进行初始化和使用,只是它们更容易构建,而且您不必销毁它们。

    【讨论】:

    • 当然,std::unique_ptr&lt;int&gt;(new int[2]); 会有错误的删除器——应该是std::unique_ptr&lt;int[]&gt;(new int[2]);。假设你会解决这个问题,抢先 +1。 ;-]
    • @ildjarn:我从来没有使用过unique_ptr 的数组,我对语法很模糊。谢谢!
    • @Evan:请务必对您认为有帮助的任何答案进行投票(左侧有向上箭头),并将其中一个标记为“正确”答案(在箭头下方带有复选标记) .
    【解决方案4】:
    for (i=0; i<5; i++)   // deallocation
          delete x[i];
    delete x;
    

    这是一个常见的错误。 x[i] 是一个数组,因此您必须先使用 delete[] 删除每个数组 然后你可以使用 delete[]

    删除你的 int* 数组

    正确的解除分配是:

    for (i=0; i<5; i++)   
          delete[] x[i]; //desallocate each array of int
    delete[] x; //desallocate your array of int*
    x = nullptr; //good practice to be sure it wont cause any dmg
    

    【讨论】:

      【解决方案5】:

      一个进一步的例子启发了我这个解决方案

      size_t k = 10;
      std::unique_ptr<int*, std::function<void(int**)>> y(new int*[k](),
          [](int** x) {delete [] &(x[0][0]);
                       delete[] x;});
      
      // Allocate the large array
      y.get()[0] = new int[k*10];
      
      // Establish row-pointers
      for (size_t row = 0; row < k; ++row) {
        (y.get())[row] = &(y.get()[0][0]);
      }
      

      这里所有维度都可以是动态的,您可以将其包装在一个类中并公开一个运算符[]。此外,内存是以连续方式分配的,您可以轻松引入分配器,分配对齐的内存。

      【讨论】:

        【解决方案6】:

        如果您没有使用std::arraystd::vector 而不是动态分配的数组的奢侈,您可以在C++11 中使用unique_ptr 作为二维数组,如下所示:

        std::unique_ptr<int*, std::function<void(int**)>> x(
            new int*[10](),
            [](int** x) {
                std::for_each(x, x + 10, std::default_delete<int[]>());
                delete[] x;
            }
        );
        

        unique_ptr 声明负责分配数组的 row 维度。 new int*[10]() 中的尾随 () 确保每个列指针都初始化为 nullptr

        for 循环然后分配列数组:

        for (size_t row = 0; row < 10; ++row) {
            (x.get())[row] = new int[5];
        }
        

        unique_ptr 超出范围时,它的自定义删除器 lambda 函数负责在删除行数组之前删除列数组。 for_each 表达式使用 default_delete 函子。

        【讨论】:

        • 可以使用_mm_malloc 扩展它吗?
        • 使用__mm_malloc分配内存,在自定义删除器中使用_mm_free
        • 谢谢,我搞定了。忘记了您的第二部分,即分配各个行(没有这个,我遇到了分段错误)。使用可变参数模板处理示例,我可以在其中创建多维数组,而无需使用 .get() 访问数据。
        【解决方案7】:

        我能想到的使用 std::unique_ptr(或者说 boost::scoped_array)而不是 std::vector 来保存数组的唯一原因通常是不适用的......

        1) 它可以节省 1 或 2 个指针的内存,具体取决于您是否知道所有数组的大小 [无关紧要,除非您有大量非常小的数组]

        2) 如果您只是将数组传递给一些需要 C 样式数组或原始指针的函数,它可能感觉更自然。虽然 std::vector 保证在顺序存储上,所以将 (a.empty() ? nullptr : &amp;a[0], a.size()) 传递给这样的函数也是 100% 合法的。

        3) 默认情况下,MSVC 调试模式下的标准容器会被“检查”并且速度非常慢,这在对大型数据集进行科学编程时可能会很烦人。

        【讨论】:

        【解决方案8】:
        for (i=0; i<5; i++)   // deallocation
              delete x[i];
          delete x;
        

        不不不不

        delete [] x[i];
        delete [] x;
        

        //哟

        【讨论】:

          猜你喜欢
          • 2013-03-01
          • 1970-01-01
          • 1970-01-01
          • 2015-05-10
          • 1970-01-01
          • 2021-12-27
          • 1970-01-01
          • 1970-01-01
          • 2021-11-12
          相关资源
          最近更新 更多