19

I have a double array allocated by pointer to pointer.

  // 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;

I am trying to do this using 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];

but kept getting an error saying that no operator = matches these operands. What I am doing wrong here?

8 Answers 8

26

You cannot assign a int* to a std::unique_ptr<int[]>, that is the cause for your error. The correct code is

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

However, piokuc is correct, that it is highly unusual to use unique_ptr for arrays, as that's what std::vector and std::array are for, depending on if the size is known ahead of time.

//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;

All of these can be initialized and used just the same as the code you already had, except they're easier to construct, and you don't have to destroy them.

Sign up to request clarification or add additional context in comments.

3 Comments

Of course, std::unique_ptr<int>(new int[2]); will have the wrong deleter -- that should be std::unique_ptr<int[]>(new int[2]);. Preemptive +1 assuming you'll fix that. ;-]
@ildjarn: I've never used unique_ptr of arrays, I'm fuzzy on the syntax. Thanks!
@Evan: Be sure to upvote any answers you found helpful (with the up arrow on the left), and mark one as the "correct" answer (with the check-mark just under the arrows).
9

If you do not have the luxury of using a std::array or a std::vector instead of a dynamically allocated array, you can use a std::unique_ptr for a two-dimensional array in C++11 as follows:

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;
    }
);

The unique_ptr declaration takes care of allocating the row dimension of the array. The trailing () in new int*[10]() ensures that each column pointer is initialized to nullptr.

A for loop then allocates the column arrays:

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

When the unique_ptr goes out of scope, its custom deleter lambda function takes care of deleting the column arrays before deleting the row array. The for_each expression uses the default_delete functor.

3 Comments

Could this be extended using _mm_malloc and how?
Use __mm_malloc to allocate the memory and use _mm_free in the custom deleter.
Thanks, I got this working. Forgot your second piece, i.e. to allocate the individual rows (without this, I got a segmentation fault). Working on an example using variadic templates, where I can create multi-dimensional arrays, without accessing data using .get().
3
for (i=0; i<5; i++)   // deallocation
      delete x[i];
  delete x;

NO NO NO NO

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

// yo

Comments

3
#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]);
    }
}

3 Comments

"#define println(x) std::cout << x << std::endl", this is what println() !
@Blastfurnace and for the rest, i asked the same question and they give me this solution and it worked so i thought it'll be helpful :)
@Blastfurnace i've edited the code, is it good now?
2

The only reasons I can think of to use std::unique_ptr (or say boost::scoped_array) over std::vector for holding arrays are usually not applicable...

1) it saves 1 or 2 pointers worth of memory, depending on if you know what the size of all the arrays is [irrelevant unless you have a massive number of very small arrays]

2) in case you are just passing the array into some function that expects a C style array or raw pointer, it may feel like a more natural fit. std::vector IS guaranteed to be on sequential storage though, so passing (a.empty() ? nullptr : &a[0], a.size()) into such a function is 100% legit as well.

3) standard containers in MSVC debug mode are "checked" by default and very slow, which might be annoying when doing scientific programming on large datasets.

1 Comment

An advantage of unique_ptr<int[]> over vector<int> is that you can avoid initialization, the cost of which may be significant in some cases. Ref stackoverflow.com/questions/96579/… and stackoverflow.com/questions/7546620/…
2

Your code is effectively manipulating an array of arrays of int.

In C++ you would normally want to implement it as:

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

This is not a good case for unique_ptr. Also, you should not need to use pointers to unique_ptr and allocate unique_ptr objects dynamically. The whole point of unique_ptr is to eliminate usage of pointers and to provide automatic allocation and deallocation of objects.

4 Comments

Thank you for your inputs. The way I understand unique_ptr is that it ensures that the instance it points to only has 1 reference. So using unique_ptr to point to an unique_ptr to create a matrix should be fine to use unique_ptr, given that there won't be another reference to the instance. Also, I don't understand the reason behind the last sentence. Thanks.
any RAII class should offer that same unique guarantee. Most C++ classes are RAII. So you should use the right tool for the job. vector and array should be preferred to unique_ptr here.
the reason for unique ptrs is mostly to hold dynamically allocated single objects. I can't immediately think of a reason to store an array in a unique_ptr.
There are some good use cases for it (surely!) since it is catered for specifically within the standard: the primary template unique_ptr< T > and specialisation unique_ptr< T[] >. I'm guessing it's not just for completeness since there is no such specialisation for shared_ptr.
0

An example further up inspired me for this solution

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]);
}

Here all dimensions can be dynamic and you can wrap it inside a class and expose an operator[]. Also the memory is allocated in a contiguous manner and you can easily introduce an allocator, which allocates aligned memory.

Comments

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

It is a common mistake here. x[i] is an array so you have to delete each array first using delete[] Then you can delete you array of int* by using delete[]

Correct desallocation would be:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.