crab oz
crab oz

Reputation: 635

Is new int[][] a valid thing to do in C++?

I have come across some code which allocates a 2d array with following approach:

auto a = new int[10][10];

Is this a valid thing to do in C++? I have search through several C++ reference books, none of them has mentioned such approach. Normally I would have done the allocation manually as follow:

int  **a = new int *[10];
for (int i = 0; i < 10; i++) {
    a[i] = new int[10];
}

If the first approach is valid, then which one is preferred?

Upvotes: 14

Views: 2625

Answers (3)

Galik
Galik

Reputation: 48635

The first example:

auto a = new int[10][10];

That allocates a multidimensional array or array of arrays as a contiguous block of memory.

The second example:

int** a = new int*[10];
for (int i = 0; i < 10; i++) {
    a[i] = new int[10];
}

That is not a true multidimensional array. It is, in fact, an array of pointers and requires two indirections to access each element.

Upvotes: 19

Thomas Russell
Thomas Russell

Reputation: 5980

In this case, for small arrays, it is more efficient to allocate them on the stack. Perhaps even using a convenience wrapper such as std::array<std::array<int, 10>, 10>. However, in general, it is valid to do something like the following:

auto arr = new int[a][b];

Where a is a std::size_t and b is a constexpr std::size_t. This results in more efficient allocation as there should only be one call to operator new[] with sizeof(int) * a * b as the argument, instead of the a calls to operator new[] with sizeof(int) * b as the argument. As stated by Galik in his answer, there is also the potential for faster access times, due to increased cache coherency (the entire array is contiguous in memory).

However, the only reason I can imagine one using something like this would be with a compile-time-sized matrix/tensor, where all of the dimensions are known at compile time, but it allocates on the heap if it exceeds the stack size.

In general, it is probably best to write your own RAII wrapper class like follows (you would also need to add various accessors for height/width, along with implementing a copy/move constructor and assignment, but the general idea is here:

template <typename T>
class Matrix {
public:
     Matrix( std::size_t height, std::size_t width ) : m_height( height ), m_width( width )
     {
           m_data = new T[height * width]();
     }

     ~Matrix() { delete m_data; m_data = nullptr; }

public:
     T&               operator()( std::size_t x, std::size_t y )
     {
          // Add bounds-checking here depending on your use-case
          // by throwing a std::out_of_range if x/y are outside 
          // of the valid domain.
          return m_data[x + y * m_width];
     }

     const T&         operator()( std::size_t x, std::size_t y ) const
     {
          return m_data[x + y * m_width];
     }

private:
     std::size_t      m_height;
     std::size_t      m_width;
     T*               m_data;
};

Upvotes: 1

user1084944
user1084944

Reputation:

The expression new int[10][10] means to allocate an array of ten elements of type int[10], so yes, it is a valid thing to do.

The type of the pointer returned by new is int(*)[10]; one could declare a variable of such a type via int (*ptr)[10];.

For the sake of legibility, one probably shouldn't use that syntax, and should prefer to use auto as in your example, or use a typedef to simplify as in

using int10 = int[10]; // typedef int int10[10];
int10 *ptr;

Upvotes: 4

Related Questions