6

標準では連続したメモリ ブロックの割り当てが強制されていないことはわかっていstd::vectorますが、それでもすべての実装がこれに従います。

多次元の静的配列のベクトルを作成したいとします。簡単にするために 2 次元と長さ N のベクトルを考えてみましょうint[5]

すべての N*5 整数がメモリ内で連続していると確信できますか? 原則として、最初の要素のアドレスを知るだけですべての整数にアクセスできるようにするには? この実装は依存していますか?

参考までに、私が現在連続したメモリ ブロックに 2D 配列を作成する方法は、最初に長さ N の float* の (動的) 配列を作成し、1 つの配列にすべての N*5 個の float を割り当ててから、5 番目ごとの要素のアドレスをの最初の配列float*

4

6 に答える 6

17

この規格では、のメモリstd::vectorが連続している必要があります。一方、次のようなものを書く場合:

std::vector<std::vector<double> > v;

グローバルメモリ(すべてv[i][j])は連続していません。2D配列を作成する通常の方法は、単一の配列を使用することです。

std::vector<double> v;

で行うことを提案するのとまったく同じように、インデックスを計算しますfloat。(必要に応じて、アドレスを使用して秒std::vector<float*>を作成することもできます。ただし、私は常にインデックスを再計算しました。)

于 2011-06-24T08:26:06.937 に答える
5

@Als がすでに指摘しているように、はい、std::vector(現在) 連続した割り当てが保証されています。ただし、ポインターの配列を使用して 2D マトリックスをシミュレートすることはしません。代わりに、2 つのアプローチのいずれかをお勧めします。(はるかに)簡単なのはoperator()、添字付けに使用し、乗算を行って2D入力をベクトルの線形アドレスに変換することです。

template <class T>
class matrix2D { 
     std::vector<T> data;
     int columns;
public:
     T &operator()(int x, int y) {
         return data[y * columns + x];
     }

     matrix2D(int x, int y) : data(x*y), columns(x) {}
};

何らかの理由でmatrix[a][b]スタイル アドレス指定を使用する場合は、プロキシ クラスを使用して変換を処理できます。2Dではなく3Dマトリックス用でしたが、この手法のデモを以前の回答に投稿しました。

于 2011-06-24T08:30:42.990 に答える
5

Vector の要素は、C++ 標準に従って連続していることが保証されています。
標準からの引用は次のとおりです。

n2798 から (C++0x のドラフト):

23.2.6 クラス テンプレート ベクター [ベクター]

1 ベクトルは、ランダム アクセス反復子をサポートするシーケンス コンテナーです。さらに、最後に(償却された)一定時間の挿入および消去操作をサポートします。途中での挿入と消去には線形時間がかかります。ストレージ管理は自動的に処理されますが、効率を改善するためのヒントを与えることができます。ベクトルの要素は連続して格納されます。つまり、T が bool 以外の型の場合、v がベクトルである場合、すべての 0 <= n < v に対して恒等式 &v[n] == &v[0] + n に従います。 。サイズ()。

C++03 標準 (23.2.4.1):

ベクトルの要素は連続して格納されます。つまり、T が bool 以外の型の場合、v がベクトルである場合、すべての 0 <= n < v に対して恒等式 &v[n] == &v[0] + n に従います。 。サイズ()。

また、同じことに関する Herb Sutter の見解も参照してください。

于 2011-06-24T08:22:03.013 に答える
3

参考までに、現在、隣接するメモリブロックに2D配列を作成する方法は、最初に長さNのfloat *の(動的)配列を作成し、1つの配列にすべてのN * 5 floatを割り当ててから、5番目ごとの要素のアドレスをにコピ​​ーすることです。 float*の最初の配列。

これは2D配列ではなく、ポインターの配列です。実際の2D配列が必要な場合は、次のように実行されます。

float (*p)[5] = new float[N][5];

p [0] [0] = 42;   // access first element
p[N-1][4] = 42;   // access last element

delete[] p;

割り当ては1つだけであることに注意してください。C ++での配列の使用についてもっと読むことをお勧めしますか?

于 2011-06-24T09:54:24.493 に答える
1

内部では、ベクトルはおおよそ (p-code) のようになります。

class vector<T> {
    T      *data;
    size_t  s;
};

これで を作るとvector<vector<T> >、このようなレイアウトになります

vector<vector<T>> --> data {
    vector<T>,
    vector<T>,
    vector<T>
};

または「インライン」形式で

vector<vector<T>> --> data {
    {data0, s0},
    {data1, s1},
    {data2, s2}
};

はい、したがって vector-vector は連続したメモリを使用しますが、そうではありません。ほとんどの場合、外部の場所へのポインター (およびその他の変数) の配列を格納します。

標準では、ベクトルのデータが連続している必要があるだけで、ベクトル全体が連続している必要はありません。

于 2011-06-24T08:40:50.023 に答える
1

2D arrayと呼ぶように、作成する単純なクラスは次のようになります。

template <class T> 2DArray {
private:
    T *m_data;
    int m_stride;
public:
    2DArray(int dimY, int dimX) : m_stride(dimX) : m_data(new[] T[dimX * dimY]) {}
    ~2DArray() { delete[] m_data; }
    T* operator[](int row) { return m_data + m_stride * row; }
}

これは次のように使用できます。

2DArray<int> myArray(30,20);

for (int i = 0; i < 30; i++)
    for (int j = 0; j < 20; j++)
        myArray[i][j] = i + j;

または&myArray[0][0]、ある種の「フラット バッファ」を使用する低レベル関数にアドレスとして渡すこともできます。

しかし、おわかりのように、それは単純な期待を覆しmyarray[y][x]ます。

一般的に、ある種の古典的な C スタイルのフラット配列を必要とするコードとやり取りする場合、それを使用しないのはなぜでしょうか?

編集:前述のように、上記は単純です。境界チェックの試行は一切ありません。まさに「配列」。

于 2011-06-24T10:03:27.920 に答える