2008-09-15 2 views
7

Я хотел бы найти безопасные способы реализации трехмерных массивов целых чисел в C++, используя распределение арифметики указателей/динамической памяти или, альтернативно, используя методы STL, такие как векторы.Трехмерные массивы целых чисел в C++

По существу я хочу, чтобы мои размеры целочисленного массива выглядеть следующим образом:

[ x ][ y ][ z ] 

х и у находятся в диапазоне 20-6000 г известен и равен 4.

+0

Копать старые кости, я знаю ... но почему люди используют C++, когда «основные» вещи, такие как многомерные массивы, предоставляются бесплатно с другими языками более высокого уровня? Это то, что вам нужно использовать C++, так что ваши руки связаны за спиной? Представление? – MikeMurko 2011-11-19 02:23:38

+0

Hi MikeMurko, в это время разум, связанный руками, был бы самым правильным. Я был бы счастлив сделать что-то подобное на Java или C#. – AndyUK 2011-12-04 16:16:43

ответ

11

Посмотри на НЧ multi-dimensional array библиотека. Вот пример (взято из документации Boost):

#include "boost/multi_array.hpp" 

int main() { 
    // Create a 3D array that is 20 x 30 x 4 
    int x = 20; 
    int y = 30; 
    int z = 4; 

    typedef boost::multi_array<int, 3> array_type; 
    typedef array_type::index index; 
    array_type my_array(boost::extents[x][y][z]); 

    // Assign values to the elements 
    int values = 0; 
    for (index i = 0; i != x; ++i) { 
    for (index j = 0; j != y; ++j) { 
     for (index k = 0; k != z; ++k) { 
     my_array[i][j][k] = values++; 
     } 
    } 
    } 
} 
5

Каждая пара квадратных скобок является операция разыменования (при нанесении на указатель). В качестве примера, следующие пары строк коды эквивалентны:

x = myArray[4]; 
x = *(myArray+4); 

 

x = myArray[2][7]; 
x = *((*(myArray+2))+7); 

Чтобы использовать синтаксис РЕКОМЕНДУЕМОГО вы просто разыменование возвращаемого значения из первого разыменования.

int*** myArray = (some allocation method, keep reading); 
// 
// All in one line: 
int value = myArray[x][y][z]; 
// 
// Separated to multiple steps: 
int** deref1 = myArray[x]; 
int* deref2 = deref1[y]; 
int value = deref2[z]; 

Чтобы идти о выделении этого массива, вы просто должны признать, что вы на самом деле не имеют трехмерный массив целых чисел. У вас есть массив массивов массивов целых чисел.

// Start by allocating an array for array of arrays 
int*** myArray = new int**[X_MAXIMUM]; 

// Allocate an array for each element of the first array 
for(int x = 0; x < X_MAXIMUM; ++x) 
{ 
    myArray[x] = new int*[Y_MAXIMUM]; 

    // Allocate an array of integers for each element of this array 
    for(int y = 0; y < Y_MAXIMUM; ++y) 
    { 
     myArray[x][y] = new int[Z_MAXIMUM]; 

     // Specify an initial value (if desired) 
     for(int z = 0; z < Z_MAXIMUM; ++z) 
     { 
      myArray[x][y][z] = -1; 
     } 
    } 
} 

Deallocating этот массив следует аналогичный процесс для выделения его:

for(int x = 0; x < X_MAXIMUM; ++x) 
{ 
    for(int y = 0; y < Y_MAXIMUM; ++y) 
    { 
     delete[] myArray[x][y]; 
    } 

    delete[] myArray[x]; 
} 

delete[] myArray; 
+0

Да, вы можете это сделать, но вы также можете сделать это в одном фрагменте памяти и только в одном распределении. Как правило, гораздо быстрее использовать один кусок памяти, что многие маленькие, косвенность памяти имеет цену. Метод, который вы показываете, в основном полезен для разреженных матриц, когда полные внутренние массивы не используются, и вы можете полностью избежать их выделения. – kriss 2010-12-16 22:56:02

1

Следует отметить, что для всех намерений и целей, вы имеете дело только с 2D-массив, так как третий (и наименее значимый) размер.

Использование STL или Boost неплохо подходит, если вы не знаете заранее, сколько записей у вас будет в каждом измерении массива, потому что они дадут вам динамическое распределение памяти, и я рекомендую любой из этих подходов, если ваш набор данных должен оставаться в основном статичным, или если он в основном получает только новые записи и не много удалений.

Однако, если вы заранее знаете что-то о своем наборе данных, например, о том, сколько всего элементов будет сохранено или если массивы должны быть малонаселенными, вам может быть полезно использовать какую-либо функцию хеша/ковша , и используйте индексы XYZ в качестве вашего ключа. В этом случае, если принять не более 8192 (13 бит) записей за измерение, вы можете получить с помощью 40-битного (5-байтового) ключа. Или, предполагая, что всегда есть 4 x Z записи, вы просто используете 26-битный ключ XY. Это один из наиболее эффективных компромиссов между скоростью, использованием памяти и динамическим распределением.

2

С векторами:

std::vector< std::vector< std::vector<int> > > array3d; 

Каждый элемент доступен остроумие array3d [х] [у] [г], если элемент уже был добавлен. (например, через push_back)

1

Существует множество преимуществ использования STL для управления вашей памятью с использованием нового/удаления. Выбор того, как представлять ваши данные, зависит от того, как вы планируете его использовать.Одним из предложений будет класс, который скрывает решение реализации и предоставляет трехмерные методы get/set для одномерного STL-вектора.

Если вы действительно считаете, что вам нужно создать пользовательский трехмерный векторный тип, сначала исследуйте Boost.

// a class that does something in 3 dimensions 

class MySimpleClass 
{ 
public: 

    MySimpleClass(const size_t inWidth, const size_t inHeight, const size_t inDepth) : 
    mWidth(inWidth), mHeight(inHeight), mDepth(inDepth) 
    { 
     mArray.resize(mWidth * mHeight * mDepth); 
    } 


    // inline for speed 
    int Get(const size_t inX, const size_t inY, const size_t inZ) { 
    return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; 
    } 

    void Set(const size_t inX, const size_t inY, const size_t inZ, const int inVal) { 
    return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; 
    } 

    // doing something uniform with the data is easier if it's not a vector of vectors 
    void DoSomething() 
    { 
    std::transform(mArray.begin(), mArray.end(), mArray.begin(), MyUnaryFunc); 
    } 

private: 

    // dimensions of data 
    size_t mWidth; 
    size_t mHeight; 
    size_t mDepth; 

    // data buffer 
    std::vector<int> mArray; 
}; 
-2

предложение Питера является, конечно, хорошо, но одна вещь, которую вы, чтобы иметь в виду, что в случае больших массивов здания может быть довольно медленным. Каждый раз, когда изменяется векторная емкость, все данные должны копироваться вокруг («n» векторов векторов).

5

Ниже приведен простой способ создания 3D-массивов с использованием C или C++ в одном блоке памяти для каждого массива. Нет необходимости использовать BOOST (даже если это хорошо) или разделить выделение между строками с множественной косвенностью (это довольно плохо, так как обычно это дает большую производительность при доступе к данным и фрагментации памяти).

Единственное, что нужно понять, это не так много, как многомерные массивы, просто массивы массивов (массивов). Самый внутренний индекс является самым большим в памяти.

#include <stdio.h> 
#include <stdlib.h> 

int main(){ 

    { 
     // C Style Static 3D Arrays 
     int a[10][20][30]; 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
    } 

    { 
     // C Style dynamic 3D Arrays 
     int (*a)[20][30]; 
     a = (int (*)[20][30])malloc(10*20*30*sizeof(int)); 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
     free(a); 
    } 

    { 
     // C++ Style dynamic 3D Arrays 
     int (*a)[20][30]; 
     a = new int[10][20][30]; 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
     delete [] a; 
    } 

} 

Для вашей актуальной проблемы, так как потенциально это два неизвестных размеров, есть проблема с моим предложением на это позволит только один неизвестному измерения. Существует несколько способов управления этим.

Хорошая новость заключается в том, что использование переменных теперь работает с C, оно называется массивами переменной длины. Вы смотрите here для деталей.

int x = 100; 
    int y = 200; 
    int z = 30; 

    { 
     // C Style Static 3D Arrays 
     int a[x][y][z]; 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
    } 

    { 
     // C Style dynamic 3D Arrays 
     int (*a)[y][z]; 
     a = (int (*)[y][z])malloc(x*y*z*sizeof(int)); 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
     free(a); 
    } 

При использовании C++, самый простой способ, вероятно, использовать перегрузку операторов придерживаться синтаксиса массива:

{ 
     class ThreeDArray { 
      class InnerTwoDArray { 
       int * data; 
       size_t y; 
       size_t z; 
       public: 
       InnerTwoDArray(int * data, size_t y, size_t z) 
        : data(data), y(y), z(z) {} 

       public: 
       int * operator [](size_t y){ return data + y*z; } 
      }; 

      int * data; 
      size_t x; 
      size_t y; 
      size_t z; 
      public: 
      ThreeDArray(size_t x, size_t y, size_t z) : x(x), y(y), z(z) { 
       data = (int*)malloc(x*y*z*sizeof data); 
      } 

      ~ThreeDArray(){ free(data); } 

      InnerTwoDArray operator [](size_t x){ 
       return InnerTwoDArray(data + x*y*z, y, z); 
      } 
     }; 

     ThreeDArray a(x, y, z); 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
    } 

Приведенный выше код имеет некоторые разыменования затраты для доступа к InnerTwoDArray (но хороший компилятор, вероятно, может оптимизировать это прочь), но использует только один кусок памяти для массива, выделенного в куче. Как правило, это самый эффективный выбор.

Очевидно, что даже если вышеуказанный код по-прежнему прост и прост, STL или BOOST делают это хорошо, поэтому нет необходимости изобретать велосипед. Я по-прежнему считаю, что интересно знать, что это легко сделать.

Смежные вопросы