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

4
  • 4
    what is grid ? shouldn't it be myarray? Commented Sep 15, 2010 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. Commented Sep 15, 2010 at 10:11
  • @MIkhail: Since justin hasn't been online since the minute he posted this question, I took the liberty to fix it. Commented Nov 13, 2010 at 12:34
  • You can use Matrix library. Commented Jun 16, 2018 at 9:34

6 Answers 6

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.

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

Comments

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.

1 Comment

Though be careful how you use those typedefs, sometimes: delete[] new array5x5() (looks like new, but it's really new[]).
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;
}

Comments

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?

Comments

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.

Comments

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.

4 Comments

Doesn't compile with GCC: the first return data[index] makes a non-const reference from an rvalue, it says.
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.
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.
@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.

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.