web-dev-qa-db-ja.com

ヒープ上のC ++多次元配列

多次元配列を動的に割り当てるにはどうすればよいですか?

22
eplawless

ネストされた次元のサイズがすでにわかっている場合は、newを使用して文字通り多次元配列を割り当てることもできます。

typedef int dimensions[3][4];

dimensions * dim = new dimensions[10];
dim[/* from 0 to 9 */][/* from 0 to 2 */][/* from 0 to 3 */] = 42;
delete [] dim;

の代わりに 10、実行時に決定された値を渡すことができます。これは型演算子newreturnsの一部ではないため、許可されています。これは、列の数はわかっているが、たとえば行の数を可変にしておきたい場合に便利です。 typedefを使用すると、コードが読みやすくなります。

これを参照してください: C++ FAQ by Marshall Cline

「newを使用して多次元配列を割り当てるにはどうすればよいですか?」を参照してください。 「しかし、前のFAQのコードは非常にトリッキーでエラーが発生しやすいです!もっと簡単な方法はありませんか?」セクション。

6
Naveen

Boost.Multiarrayを使用するのはどうですか?私はそれがあなたのニーズに非常によく答えると信じています! http://www.boost.org/doc/libs/1_37_0/libs/multi_array/doc/user.html#sec_introduction

これはドキュメントページからの抜粋です:

 #include < boost/multi_array.hpp >

 #include < cassert >

int main () 

{

  // Create a 3D array that is 3 x 4 x 2

  typedef boost::multi_array< double, 3 > array_type;

  typedef array_type::index index;

  array_type A(boost::extents[3][4][2]);


  // Assign values to the elements

  int values = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        A[i][j][k] = values++;

  // Verify values

  int verify = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        assert(A[i][j][k] == verify++);

  return 0;

}
6
Benoît

完全を期すために、配列の境界が事前にわかっている場合は、C++でこれを行うためのより良い方法を次に示します。次のクラスを使用する利点は、データに対してdelete []を呼び出す必要がないことです。これは、このクラスが例外安全であり、 [〜#〜] raii [〜#〜] に関する他のすべての優れた機能があることを意味します。

template<typename T, int width, int height>
class MultiArray
{
    private:
        typedef T cols[height];
        cols * data;
    public:
        T& operator() (int x, int y) { return data[x][y]; }
        MultiArray() { data = new cols[width]; }
        ~MultiArray() { delete [] data; }
};

使用法:

MultiArray<int, 10, 10> myArray;
myArray(2, 3) = 4;
cout << myArray(2, 3);

編集: そして、私がそれにいる間、あなたがあなたがするならばあなたが使うことができるセットアップはここにあります しないでください 実行時まで配列の境界を知っている:

template<typename T>
class Array2D
{
    private:
        const int width;
        T * data;
    public:
        T& operator() (int x, int y) { return data[y*width + x]; }
        Array2D(const int w, const int h) : width(w) { data = new T[w*h]; }
        ~Array2D() { delete [] data; }
};

使用法:

Array2D myArray(10, 10);
myArray(3, 4) = 42;
cout << myArray(3, 4);
6
e.James

誰も言及していないことに驚いていますboost::multi_arrayまだ。先週、プログラムに2D配列が必要でしたが、これまでに思いついた自家製のソリューションよりもはるかに簡単で、コーディングも迅速であることがわかりました(すべて他のコメントで言及されています)。 。

3
Head Geek

std::vector<std::vector<int> >最も簡単な方法であることが多いため、言及する必要があります。ただし、長方形ではないことに注意してください。すべてではない std::vector<int>同じ長さである必要があります。

3
MSalters

これが私が持っている実装です。 forループ内に新しいブロックを作成する代わりに、intsの単一の連続ブロックを宣言するので、ページフォールトが発生することはありません。このコードが最初に壊れた理由を指摘してくれたeJamesに感謝します。

int width = 10, height = 10, totalSize = width*height;
int **myArray = new int*[width];
int *data = new int[totalSize];

for ( int i = 0; i < height; ++i )
{
    myArray[i] = data + (i*width);
}

// do some things here

delete[] data;
delete[] myArray;
3
eplawless

ループはポインタ値をmyArrayに正しく書き込みません。代わりに、次のことをお勧めします。

int width = 10;
int height = 10;
int ** myArray = new int*[width];
int * data = new int[width*height];
int * index = data;
for (int i = 0; i < width; i++)
{
    myArray[i] = index;
    index += height;
}

// ...

delete[] data;
delete[] myArray;
2
e.James

別の代替手段として、 STLSoft には fixed_array_2d クラス(および3Dおよび4Dバージョン)が含まれます。ここに示した自作のソリューションと比較すると、実装は似ていますが、より完全な機能セット(イテレーターの完全サポートなど)があります。 boost :: multi_arrayと比較すると、非準拠のC++コンパイラでは軽量で簡単ですが、(意図的に)multi_arrayの機能の一部が欠けています。

2
Josh Kelley

これは別のスレッドの投稿の複製です。事前に配列の寸法を知る必要がなく、ブーストやSTLを使用せずに、必要なことを正確に実行します。

これは、次元N1 x N2 x N3の3D配列を連続したメモリ空間に割り当て、演算子アクセス用のa [i] [j] [k]構文を許可するルーチンです。配列は動的ですが連続的であるため、vector <>アプローチとnew []呼び出しのループに大きなプラスになります。

template <class T> T ***Create3D(int N1, int N2, int N3)
{
    T *** array = new T ** [N1];

    array[0] = new T * [N1*N2];

    array[0][0] = new T [N1*N2*N3];

    int i,j,k;

    for( i = 0; i < N1; i++) {

        if (i < N1 -1 ) {

            array[0][(i+1)*N2] = &(array[0][0][(i+1)*N3*N2]);

            array[i+1] = &(array[0][(i+1)*N2]);

        }

        for( j = 0; j < N2; j++) {     
            if (j > 0) array[i][j] = array[i][j-1] + N3;
        }

    }

    cout << endl;
    return array;
};

template <class T> void Delete3D(T ***array) {
    delete[] array[0][0]; 
    delete[] array[0];
    delete[] array;
};

そして、後で実装ルーチンで...

int *** array3d;
int N1=4, N2=3, N3=2;

int elementNumber = 0;

array3d = Create3D<int>(N1,N2,N3);

//equivalently, a 'flat' array could be obtained with
//int * array = array3d[0][0];

cout << "{" << endl;
for (i=0; i<N1; i++) {
    cout << "{";
    for (j=0; j<N2; j++) {
        cout << "{";
        for (k=0; k<N3; k++) {
            array3d[i][j][k] = elementNumber++;
            cout << setw(4) << array3d[i][j][k] << " ";

            //or if you're using the flat array:
            //array[i*N2*N3 + j*N3 + k] = elementNumber++;

        }
        cout << "}";
    }
    cout << "}";
    cout << endl ;
}
cout << "}" << endl;

Delete3D(array3d);

出力を提供します:

{
{{   0    1 }{   2    3 }{   4    5 }}
{{   6    7 }{   8    9 }{  10   11 }}
{{  12   13 }{  14   15 }{  16   17 }}
{{  18   19 }{  20   21 }{  22   23 }}
}
0
v.chaplin

私の質問 ここ いくつかの素晴らしい答えのおかげで、ほとんど同じトピックをかなりうまくカバーしています。ただし、ここでの回答でも取り上げられていないN次元配列については説明していませんが、それは便利です。

0

正しい量の要素の上にスペースを置くだけで、1次元を2、3、またはN次元としてインデックス付けできます。たとえば、10行10列の場合、行3にいる場合は、そこに到達するために少なくとも30個の要素を調べる必要があることがわかっています。

ポインタのネストされたレベルについて心配する必要がないので、どういうわけか私は単純な2D配列に対してこの表記法を好みます。欠点は、より厄介なインデックス表記です。 n行m列の2D配列の例を次に示します。

int *matrix = new int[n*m];

//set element (3,7) to 10
matrix[3*m+7] = 10;

//print the matrix
for (int i = 0; i < n; i++) {
  for (int j = 0; j < m; j++) {
    cout << matrix[i*m+j] << ' ';
  }
  cout << '\n';
}
0
Rich