开发者

elegant way to create&pass multi-dimensional array in c++?

first question:

for known dimensions, we don't need new/malloc for the creation

   const int row = 3;  
   const int col = 2;  
   int tst_matrix[row][col] ={{1,2},{3,4},{5,6}}

however, there is no easy to pass this two-dimensional array to another function, right? because

   int matrix_process(int in_matrix[][])

is illegal, you have to specify all the dimensions except the first one. if I need to change the content of in_matrix, how could I easily pass tst_matrix to the function matrix_process?

second question: what's the standard way to create 2-dimensional array in c++ with new? I dont wanna use std::vector etc.. here. here is what I come up with, is it the best way?

    int **tst_arr = new int*[5];
    int i=0, j=0;

    for (i=0;i<5;i++)
    {
       tst_arr[i] = new int[5];
       for (j=0;j<5;j++)
       {
          tst_arr[i][j] = i*5+j;
       }
    }

In addition, if I pass tst_array to another function, like:

     int change_row_col( int **a)       
     {
         .....................
         //check which element is 0
         for (i=0; i<5; i++)
            for(j=0;j<5;j++)
           {
             if (*(*(a+i)+j)==0)  //why I can not use a[i][j] here?
             {
               row[i]=1;
               col[j]=1;              
             }
           }
         .....................
     }

In addition, if I use ((a+i)+j), the result is not what I want. Here is the complete testing code I had:

    #include <iostream>

    using namespace std;

    //Input Matrix--a: Array[M][N]

    int change_row_col( int **a开发者_C百科)
    {
     int i,j;
     int* row = new int[5];
     int* col = new int[5];

     //initialization
     for(i=0;i<5;i++)
     {
        row[i]=0;
     }

     for(j=0;j<5;i++)
     {
        col[j]=0;
     }

     //check which element is 0
     for (i=0; i<5; i++)
         for(j=0;j<5;j++)
        {
           if (*(*(a+i)+j)==0)  //why I can not use a[i][j] here?
           {
               row[i]=1;
               col[j]=1;              
           }
        }

     for(i=0;i<5;i++)
       for (j=0;j<5;j++)
       {
            if (row[i] || col[j])    
            {
               *(*(a+i)+j)=0;
            }
       }
     return 1;
 }



int main ()
{
    int **tst_arr = new int*[5];
    int i=0, j=0;

    for (i=0;i<5;i++)
    {
       tst_arr[i] = new int[5];
       for (j=0;j<5;j++)
       {
          tst_arr[i][j] = i*5+j;
       }
    }

  for (i=0; i<5;i++)
  {
    for(j=0; j<5;j++)
    {
       cout<<" "<<tst_arr[i][j];
    }
    cout<<endl;
  }
 change_row_col(tst_arr);

 for (i=0; i<5;i++)
 {
     for(j=0; j<5;j++)
     {
        cout<<" "<<tst_arr[i][j];
     }
     cout<<endl;
 }

   for (i=0;i<5;i++)
   {
      delete []tst_arr[i];
   }
   delete []tst_arr;
 }


For multidimensional arrays were all the bounds are variable at run time, the most common approach that I know of is to use a dynamically allocated one dimensional array and do the index calculations "manually". In C++ you would normally use a class such as a std::vector specialization to manage the allocation and deallocation of this array.

This produces essentially the same layout as a multidimensional array with fixed bounds and doesn't have any real implied overhead as, without fixed bounds, any approach would require passing all bar one of the array dimensions around at run time.


I honestly think the best idea is to eschew raw C++ arrays in favor of a wrapper class like the boost::multi_array type. This eliminates all sorts of weirdness that arises with raw arrays (difficulty passing them S parameters to functions, issues keeping track of the sizes of the arrays, etc.)

Also, I strongly urge you to reconsider your stance on std::vector. It's so much safer than raw arrays that there really isn't a good reason to use dynamic arrays over vectors in most circumstances. If you have a C background, it's worth taking the time to make the switch.


My solution using function template:

template<size_t M,size_t N>
void Fun(int (&arr)[M][N])
{
   for ( int i = 0 ; i < M ; i++ )
   {
       for ( int j = 0 ; j < N ; j++ )
       {
           /*................*/
       }
   }
}


1)

template < typename T, size_t Row_, size_t Col_>
class t_two_dim {
public:
    static const size_t Row = Row_;
    static const size_t Col = Col_;
    /* ... */
    T at[Row][Col];
};

template <typename T>
int matrix_process(T& in_matrix) {
    return T::Row * T::Col + in_matrix.at[0][0];
}

2) use std::vector. you're adding a few function calls (which may be inlined in an optimized build) and may be exporting a few additional symbols. i suppose there are very good reasons to avoid this, but appropriate justifications are sooooo rare. do you have an appropriate justification?


The simple answer is that the elegant way of doing it in C++ (you tagged C and C++, but your code is C++ new/delete) is by creating a bidimensional matrix class and pass that around (by reference or const reference). After that, the next option should always be std::vector (and again, I would implement the matrix class in terms of a vector). Unless you have a very compelling reason for it, I would avoid dealing with raw arrays of arrays.

If you really need to, but only if you really need to, you can perfectly work with multidimensional arrays, it is just a little more cumbersome than with plain arrays. If all dimensions are known at compile time, as in your first block this are some of the options.

const unsigned int dimX = ...;
const unsigned int dimY = ...;
int array[dimY][dimX];
void foo( int *array[dimX], unsigned int dimy ); // [1]
void foo( int (&array)[dimY][dimX] );            // [2]

In [1], by using pass-by-value syntax the array decays into a pointer to the first element, which means a pointer into an int [dimX], and that is what you need to pass. Note that you should pass the other dimension in another argument, as that will be unknown by the code in the function. In [2], by passing a reference to the array, all dimensions can be fixed and known. The compiler will ensure that you call only with the proper size of array (both dimensions coincide), and thus no need to pass the extra parameter. The second option can be templated to accomodate for different sizes (all of them known at compile time):

template <unsigned int DimX, unsigned int DimY>
void foo( int (&array)[DimY][DimX] );

The compiler will deduct the sizes (if a real array is passed to the template) and you will be able to use it inside the template as DimX and DimY. This enables the use of the function with different array sizes as long as they are all known at compile time.

If dimensions are not known at compile time, then things get quite messy and the only sensible approach is encapsulating the matrix in a class. There are basically two approaches. The first is allocating a single contiguous block of memory (as the compiler would do in the previous cases) and then providing functions that index that block by two dimensions. Look at the link up in the first paragraph for a simple approach, even if I would use std::vector instead of a raw pointer internally. Note that with the raw pointer you need to manually manage deletion of the pointer at destruction or your program will leak memory.

The other approach, which is what you started in the second part of your question is the one I would avoid at all costs, and consists in keeping a pointer into a block of pointers into integers. This complicates memory management (you moved from having to delete a pointer into having to delete DimY+1 pointers --each array[i], plus array) and you also need to manually guarantee during allocation that all rows contain the same number of columns. There is a substantial increase in the number of things that can go wrong and no gain, but some actual loss (more memory required to hold the intermediate pointers, worse runtime performance as you have to double reference, probably worse locality of data...

Wrapping up: write a class that encapsulates the bidimensional object in terms of a contiguous block of memory (array if sizes are known at compile time --write a template for different compile time sizes--, std::vector if sizes are not known until runtime, pointer only if you have a compelling reason to do so), and pass that object around. Any other thing will more often than not just complicate your code and make it more error prone.


For your first question:

If you need to pass a ND array with variable size you can follow the following method to define such a function. So, in this way you can pass the required size arguments to the function. I have tested this in gcc and it works.

Example for 2D case:

void editArray(int M,int N,int matrix[M][N]){

//do something here

}
int mat[4][5];

editArray(4,5,mat);  //call in this way
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜