25

How do I return a multidimensional array stored in a private field of my class?

class Myclass {
private:
   int myarray[5][5];
public:
   int **get_array();
};

// This does not work:
int **Myclass::get_array() {
    return myarray;
}

I get the following error:

cannot convert int (*)[5][5] to int** in return

Holt
  • 36,600
  • 7
  • 92
  • 139
justin
  • 259
  • 1
  • 3
  • 3
  • 4
    what is grid ? shouldn't it be `myarray`? – woodstok Sep 15 '10 at 10:08
  • 3
    Returning a direct reference to a private member may not always be a good idea - you effectively break encapsulation by this, allowing anyone to access and modify your private member. This may or may not be acceptable in your design. – Péter Török Sep 15 '10 at 10:11
  • @MIkhail: Since justin hasn't been online since the minute he posted this question, I took the liberty to fix it. – sbi Nov 13 '10 at 12:34
  • You can use [Matrix library](https://stackoverflow.com/a/50886558/7580839). – Amir Fo Jun 16 '18 at 09:34

6 Answers6

28

A two-dimensional array does not decay to a pointer to pointer to ints. It decays to a pointer to arrays of ints - that is, only the first dimension decays to a pointer. The pointer does not point to int pointers, which when incremented advance by the size of a pointer, but to arrays of 5 integers.

class Myclass {
private:
    int myarray[5][5];
public:
    typedef int (*pointer_to_arrays)[5]; //typedefs can make things more readable with such awkward types

    pointer_to_arrays get_array() {return myarray;}
};

int main()
{
    Myclass o;
    int (*a)[5] = o.get_array();
    //or
    Myclass::pointer_to_arrays b = o.get_array();
}

A pointer to pointer (int**) is used when each subarray is allocated separately (that is, you originally have an array of pointers)

int* p[5];
for (int i = 0; i != 5; ++i) {
    p[i] = new int[5];
}

Here we have an array of five pointers, each pointing to the first item in a separate memory block, altogether 6 distinct memory blocks.

In a two-dimensional array you get a single contiguous block of memory:

int arr[5][5]; //a single block of 5 * 5 * sizeof(int) bytes

You should see that the memory layout of these things are completely different, and therefore these things cannot be returned and passed the same way.

visitor
  • 8,564
  • 2
  • 26
  • 15
18

There are two possible types that you can return to provide access to your internal array. The old C style would be returning int *[5], as the array will easily decay into a pointer to the first element, which is of type int[5].

int (*foo())[5] {
   static int array[5][5] = {};
   return array;
}

Now, you can also return a proper reference to the internal array, the simplest syntax would be through a typedef:

typedef int (&array5x5)[5][5];
array5x5 foo() {
   static int array[5][5] = {};
   return array;
}

Or a little more cumbersome without the typedef:

int (&foo())[5][5] {
   static int array[5][5] = {};
   return array;
}

The advantage of the C++ version is that the actual type is maintained, and that means that the actual size of the array is known at the callers side.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    Though be careful how you use those typedefs, sometimes: `delete[] new array5x5()` (looks like new, but it's really new[]). –  Nov 13 '10 at 10:45
8

To return a pointer to your array of array member, the type needed is int (*)[5], not int **:

class Myclass {
private:
    int myarray[5][5];
public:
    int (*get_array())[5];
};

int (*Myclass::get_array())[5] {
    return myarray;
}
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
2

How do I return a multidimensional array hidden in a private field?

If it's supposed to be hidden, why are you returning it in the first place?

Anyway, you cannot return arrays from functions, but you can return a pointer to the first element. What is the first element of a 5x5 array of ints? An array of 5 ints, of course:

int (*get_grid())[5]
{
    return grid;
}

Alternatively, you could return the entire array by reference:

int (&get_grid())[5][5]
{
    return grid;
}

...welcome to C declarator syntax hell ;-)

May I suggest std::vector<std::vector<int> > or boost::multi_array<int, 2> instead?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
2

Simpler would be decltype(auto) (since C++14)

decltype(auto) get_array() { return (myarray); } // Extra parents to return reference

then decltype (since C++11) (member should be declared before the method though)

auto get_array() -> decltype((this->myarray)) { return myarray; }
// or
auto get_array() -> decltype(this->myarray)& { return myarray; }

then typedef way:

using int2D = int[5][5]; // since C++11
// or
typedef int int2D[5][5];

int2D& get_array() { return myarray; }

as regular syntax is very ugly way:

int (&get_array())[5][5] { return myarray; }

Using std::array<std::array<int, 5>, 5> (since C++11) would have more natural syntax.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

I managed to make this function work in C++0x using automatic type deduction. However, I can't make it work without that. Native C arrays are not supported very well in C++ - their syntax is exceedingly hideous. You should use a wrapper class.

template<typename T, int firstdim, int seconddim> class TwoDimensionalArray {
    T data[firstdim][seconddim];
public:
    T*& operator[](int index) {
        return data[index];
    }
    const T*& operator[](int index) const {
        return data[index];
    }
};
class Myclass {
public:
    typedef TwoDimensionalArray<int, 5, 5> arraytype;
private:
    arraytype myarray;
public:
    arraytype& get_array() {
        return myarray;
    }
};

int main(int argc, char **argv) {
    Myclass m;
    Myclass::arraytype& var = m.get_array();
    int& someint = var[0][0];
}

This code compiles just fine. You can get pre-written wrapper class inside Boost (boost::array) that supports the whole shebang.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Doesn't compile with GCC: the first return data[index] makes a non-const reference from an rvalue, it says. – Cubbi Sep 15 '10 at 10:30
  • And that is the case, a bare pointer should be returned, not a reference to a pointer. Otherwise, we have at least gain much in readability. One minor point: why using `int` for the actual template types ? Something that cannot possibly be negative would be better expressed with an unsigned integer type I think. – Matthieu M. Sep 15 '10 at 11:49
  • data[index] is an lvalue, not an rvalue, much like *ptr is an lvalue. Not that the reference is really necessary, I think it's from a previous version of the code, you could probably remove it. – Puppy Sep 15 '10 at 12:41
  • @Matthieu: You don't have to prevent against negative numbers here (the compiler will do that for you) and int makes a better default int type: e.g. cases where you want the difference of two sizes. –  Nov 13 '10 at 10:51