【问题标题】:elegant way to create&pass multi-dimensional array in c++?在 C++ 中创建和传递多维数组的优雅方式?
【发布时间】:2011-06-07 00:14:28
【问题描述】:

第一个问题:

对于已知维度,我们不需要 new/malloc 来创建

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

但是,将这个二维数组传递给另一个函数并不容易,对吧?因为

   int matrix_process(int in_matrix[][])

是非法的,您必须指定除第一个之外的所有维度。如果我需要更改 in_matrix 的内容,如何轻松将 tst_matrix 传递给函数 matrix_process?

第二个问题: 在 C++ 中使用 new 创建二维数组的标准方法是什么?我不想在这里使用 std::vector 等。 这是我想出的,这是最好的方法吗?

    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;
       }
    }

另外,如果我将 tst_array 传递给另一个函数,比如:

     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;              
             }
           }
         .....................
     }

另外,如果我使用((a+i)+j),结果不是我想要的。 这是我拥有的完整测试代码:

    #include <iostream>

    using namespace std;

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

    int change_row_col( int **a)
    {
     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;
 }

【问题讨论】:

  • 你为什么不想使用std::vector
  • "我不想使用 std::vector" 你有什么好的理由吗?
  • 好吧,如果你不想使用 std::vector,总是有 boost::array。
  • 不幸的是,C 和 C++ 在这方面存在分歧。 Modern C (C99) 有可变长度的数组,这将非常适合您的目的。
  • “在 C++ 函数中传递多维数组的优雅方式”是使用“std::vector”。但是,从您的帖子中,我了解到您不想使用这种优雅。

标签: c++ pointers multidimensional-array


【解决方案1】:

对于多维数组,所有边界在运行时都是可变的,我所知道的最常见的方法是使用动态分配的一维数组并“手动”进行索引计算。在 C++ 中,您通常会使用诸如 std::vector 特化之类的类来管理此数组的分配和解除分配。

这会产生与具有固定边界的多维数组基本相同的布局,并且没有任何真正的隐含开销,因为如果没有固定边界,任何方法都需要在运行时传递所有数组维度之一。

【讨论】:

  • 第二。分配指针数组,然后为每个指针分配整数数组的方法,如示例代码中所示,是存储任何大小数组的一种非常低效的方法。我要添加到您的答案中的一件事是您应该定义一个执行索引计算的访问器函数(我建议使用两个参数的operator()),而不是在每次查找时手动进行。
【解决方案2】:

老实说,我认为最好的办法是避开原始 C++ 数组,转而支持像 boost::multi_array 类型这样的包装类。这消除了原始数组引起的各种怪异(难以将 S 参数传递给函数,跟踪数组大小的问题等)

另外,我强烈敦促您重新考虑您对 std::vector 的立场。它比原始数组安全得多,以至于在大多数情况下没有充分的理由在向量上使用动态数组。如果您有 C 背景,那么值得花时间进行转换。

【讨论】:

  • +1 用于避免 NIH 综合征。还要考虑 boost::array。根据您的具体需求,以各种可能的方式混合和匹配 std::vector、boost::array 和 boost::multi_array 可能是完全正确的。
【解决方案3】:

我使用函数模板的解决方案:

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++ )
       {
           /*................*/
       }
   }
}

【讨论】:

  • 这仍然只适用于已知尺寸;它只适用于 任何 已知尺寸。在运行时确定尺寸,这不是解决方案。
  • @Charles:我完全同意。对于运行时已知的维度,必须选择您建议的内容,或者编写自己的Array&lt;&gt; 类模板(也使用一些元编程)。去年,我的最后一个项目要求我写这个,所以我写了Array&lt;&gt; 来完成我的工作。
【解决方案4】:

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) 使用 std::vector。您正在添加一些函数调用(可能在优化的构建中内联)并且可能正在导出一些额外的符号。我想有很好的理由来避免这种情况,但适当的理由很少见。你有合适的理由吗?

【讨论】:

    【解决方案5】:

    简单的答案是在 C++ 中执行此操作的优雅方式(您标记了 C 和 C++,但您的代码是 C++ new/delete)是通过创建二维 matrix 类并将其传递(通过引用或常量引用)。在那之后,下一个选项应该始终是std::vector(同样,我将根据向量来实现矩阵类)。除非你有一个非常令人信服的理由,否则我会避免处理原始数组。

    如果你真的需要,但只有你真的需要,你可以完美地使用多维数组,它只是比普通数组更麻烦一点。如果在编译时所有维度都是已知的,就像在你的第一个块中一样,这是一些选项。

    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]
    

    在 [1] 中,通过使用 pass-by-value 语法,数组衰减为指向第一个元素的指针,这意味着指向 int [dimX] 的指针,这就是您需要传递的内容。请注意,您应该在另一个参数中传递另一个维度,因为函数中的代码将不知道该维度。在 [2] 中,通过将 reference 传递给数组,所有维度都可以是固定和已知的。编译器将确保您仅使用适当大小的数组(两个维度重合)调用,因此无需传递额外的参数。第二个选项可以模板化以适应不同的大小(在编译时都知道):

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

    编译器将扣除大小(如果将 real 数组传递给模板),您将能够在模板中以 DimXDimY 的形式使用它。这允许使用具有不同数组大小的函数,只要它们在编译时都是已知的。

    如果在编译时不知道维度,那么事情会变得非常混乱,唯一明智的方法是将矩阵封装在一个类中。基本上有两种方法。第一个是分配一个连续的内存块(就像编译器在前面的例子中所做的那样),然后提供按二维索引该块的函数。查看第一段中的链接以获得简单的方法,即使我会在内部使用 std::vector 而不是原始指针。请注意,对于原始指针,您需要在销毁时手动管理指针的删除,否则您的程序会泄漏内存。

    另一种方法,即您在问题的第二部分开始的方法,是我将不惜一切代价避免的方法,它包括将指针保留在指向整数的指针块中。这使内存管理复杂化(您从必须使用delete 指针变为必须删除DimY+1 指针--每个array[i],加上array),并且您还需要在分配期间手动保证所有行都包含相同的列数。可能出错且没有收益的事情的数量大幅增加,但实际上有一些损失(保存中间指针所需的内存更多,运行时性能更差,因为你必须双重引用,可能更糟糕的数据局部性......

    总结:编写一个类,根据连续的内存块封装二维对象(如果大小在编译时已知,则为数组--为不同的编译时大小编写模板--,std::vector 如果大小是直到运行时才知道,只有当你有令人信服的理由这样做时才知道指针),并传递该对象。任何其他事情往往不仅会使您的代码复杂化并使其更容易出错。

    【讨论】:

      【解决方案6】:

      第一个问题:

      如果你需要传递一个可变大小的ND数组,你可以按照下面的方法来定义这样一个函数。因此,通过这种方式,您可以将所需的大小参数传递给函数。 我已经在 gcc 中测试过了,它可以工作。

      2D 案例示例:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-17
        • 1970-01-01
        • 1970-01-01
        • 2016-05-02
        • 1970-01-01
        • 2010-09-30
        • 1970-01-01
        相关资源
        最近更新 更多