blackløtus
blackløtus

Reputation: 899

Declare a multidimentional array without the size

I want to declare an array of arrays or multidimensional array without knowing the size.

I want to do something similar to what I did in this cases with simple arrays:

int *array;
cin >> size;
array = new int[size];

Maybe I can loop to initialize a pointer of pointers like this:

int **array;
cin >> rows >> col;
array = new *int[rows]
for (int i = 0; i < rows; ++i)
    array[i] = new int[col];

But I would prefer to not do this if a better solution exists.

Upvotes: 7

Views: 22304

Answers (4)

amdn
amdn

Reputation: 11582

You could use a single std::vector to contain the entire two dimensional array and wrap it in a class to hide the details. Here's an example, it uses a data( row, col ) member function that returns a reference to the element at row and col. I included an example 2 dimensional matrix of int where each entry in the array is initialized to the product of its row and col. When an instance of this class goes out of scope, the default destructor will get called and release the memory, that way you don't have to remember to call delete[] to release the memory. All elements of the matrix will be contiguous in memory, this is cache friendly and should give you good performance.

#include <iostream>
#include <vector>
#include <stdexcept>

template <typename T>
class matrix {
    std::vector<T> data_;
public:
    size_t const rows_;
    size_t const cols_;
    matrix(size_t rows, size_t cols)
        : rows_(rows)
        , cols_(cols)
        , data_( rows * cols )
    {}
    T& data( size_t row, size_t col ) {
        if (row > rows_ || col > cols_) throw std::out_of_range("matrix");
        return data_[ row * cols_ + col ];
    }
};

int main( int argc, char** argv )
{
    matrix<int> array(100,100);

    for(size_t r=0; r < array.rows_; ++r) {
        for(size_t c=0; c < array.cols_; ++c) {
            array.data(r,c) = r * c;
        }
    }

    std::cout << "8 x 7 = " << array.data(8,7) << std::endl;

    return 0; // array goes out of scope here, memory released automatically
}

When you run this you will get

8 x 7 = 56

Upvotes: 1

sehe
sehe

Reputation: 392931

If you care, you can have a little bit more convenience by have a helper

template <typename T>
struct C3DArray
{
    vector<vector<vector<T>>> m;
    C3DArray(int size_x, int size_y, int size_z)
        : m(make(T(), size_z, size_y, size_x))
    { }

    template <typename U> static std::vector<U> make(U v, size_t n) {
        return { n, std::move(v) };
    }

    template <typename U, typename... Dim> static auto make(U v, size_t n, Dim... other)
        -> std::vector<decltype(make(v, other...))> {
        return { n, make(v, other...) };
    }
};

This uses variadics. Use it like this:

C3DArray<int> arr(3,4,20);

Upvotes: 0

sblom
sblom

Reputation: 27343

You're pretty much going to have to go with the loop version. You can make one slight improvement, which is to allocate one big block and then build your own int* index into it:

int **array;
int *storage;
cin >> rows >> col;
array = new *int[rows];
storage = new int[rows*col];
for (int i = 0; i < rows; ++i)
    array[i] = storage + col * i;

This has the nice property that you can still use array[i][j] syntax for accessing the array.

Upvotes: 2

Geoff Montee
Geoff Montee

Reputation: 2597

Why not use std::vector?

std::vector<std::vector<int> > array;

If you don't want to use an array of pointers, you could use one large array that you allocate dynamically after you get the size and access it as an array of rows.

int rows = 10;
int columns = 20;

int* array = new int[rows * columns];

for (int count = 0; count < rows; count++)
{
   int* row = &array[count * columns];

   for (int inner_count = 0; inner_count < columns; inner_count++)
   {
      int* element = &row[inner_count];

      //do something
   }
}

delete [] array;

Upvotes: 6

Related Questions