C++ Dynamic/Static arrays as parameters to functio

2020-04-23 04:48发布

问题:

I have been trying to use 2D array as a parameter to a constructor in my C++ class.

Header:

Matrix::Matrix(double **initComponents, int rows, int columns)

If I just go ahead and do regular calls to new and initialize sub arrays, pass a pointer to a constructor, its all good BUT I was thinking , can I just create static 2D array with initializer and pass a pointer to it, to a constructor?

double A[][3] = { {2.0, 3.0, 4.0} , {1.0, 3.0, 4.0} , {2.0, 3.0, 4.0} }; 
Matrix *mtx = new Matrix(A, 3, 3);

that does not work :(

MS Visual Studio compiler :

" error C2664: 'Matrix::Matrix(double **,int,int)' : cannot convert parameter 1 from 'double [3][3]' to 'double ** "

Can anyone explain why? I thought it will do an automatic conversion between reference and a pointer.

Second question is, why can I just declare A as double A[][], why do I have to specify the second dimension, even though I specify all values.

回答1:

An array does convert to a pointer, but it only works for one level. An array of arrays does not convert to a pointer to pointers.

An array can be converted to a pointer because a pointer still provides a reasonable way to look up the values of the array, but an array of arrays is a fundamentally different thing than a pointer to pointers. An array of arrays is stored in memory contiguously. For example, if you had a definition like this:

int a[2][2] = {{1,2},{3,4}};

Then a[0][0] is stored in the first memory location, a[0][1] is stored in the second, a[1][0] in the third, and a[1][1] in the fourth. When you want to look up a particular element like a[i][j], the compiler can compute the location since it knows the size of the arrays:

int value = ((int *)a)[i*2+j];

Compare this to a pointer to pointers

int a0[2] = {1,2};
int a1[2] = {3,4};
int **a = {a0,a1};

Now when you use a[i][j], the compiler can't directly know where to look. It first has to look at a[0] and see where that points to, and then look a second time to find the value.

int *ap = a[i];
int value = ap[j];

Because the compiler has to use a different way of looking up the values in arrays of arrays and pointers to pointers, you can't use them interchangeably.

Likewise, in a declaration like this:

int a[][2] = {{1,2},{3,4}};

it is okay to leave off the size of the first dimension because the compiler will fill it in for you, but this only works for one level. In principle, the compiler could make a good guess about what the other dimensions should be, but it would really be a separate rule in the language.

If you really want to be able to pass a 2D array, you can do it with a templated constructor like this:

template<int rows, int cols>
Matrix(const double (&initComponents)[rows][cols])
{
  // initialize matrix elements here
}

And then you don't even need to pass the size explicitly:

double A[][3] = { {2.0, 3.0, 4.0} , {1.0, 3.0, 4.0} , {2.0, 3.0, 4.0} };
Matrix *mtx = new Matrix(A);