Доступ к вектору‹vector‹int›› как к плоскому массиву

Для этого массива:

vector<vector<int> > v;

v.push_back(vector<int>(0));
v.back().push_back(1);
v.back().push_back(2);
v.back().push_back(3);
v.back().push_back(4);

Я могу достаточно легко вывести {1, 2, 3, 4}:

cout << v[0][0] << endl;
cout << v[0][1] << endl;
cout << v[0][2] << endl;
cout << v[0][3] << endl;

Чтобы получить к нему доступ как к плоскому массиву, я могу сделать это:

int* z = (int*)&v[0].front();

cout << z[0] << endl;
cout << z[1] << endl;
cout << z[2] << endl;
cout << z[3] << endl;

Теперь, как мне получить доступ к многомерному вектору как к плоскому многомерному массиву? Я не могу использовать тот же формат, что и для доступа к одномерному вектору:

// This does not work (outputs garbage)
int** n = (int**)&v.front();

cout << n[0][0] << endl;
cout << n[0][1] << endl;
cout << n[0][2] << endl;
cout << n[0][3] << endl;

Обходной путь, который я нашел, заключается в следующем:

int** n = new int* [v.size()];

for (size_t i = 0; i < v.size(); i++) {
   n[i] = &v.at(i).front();
}

cout << n[0][0] << endl;
cout << n[0][1] << endl;
cout << n[0][2] << endl;
cout << n[0][3] << endl;

Есть ли способ получить доступ ко всему многомерному вектору, такому как плоский массив c-стиля, без необходимости динамического выделения каждого измерения над данными перед доступом к ним?

Скорость не имеет решающего значения при реализации, а ясность для обслуживания имеет первостепенное значение. Многомерный вектор отлично подходит для хранения данных. Однако я также хочу предоставить данные в виде плоского массива в стиле c в SDK, чтобы к ним можно было легко получить доступ из других языков. Это означает, что выставлять векторы как объект STL нельзя.

Решение, которое я придумал, отлично подходит для моих нужд, поскольку я оцениваю массив только один раз в самом конце обработки, чтобы «сгладить» его. Однако есть ли лучший способ сделать это? Или я уже делаю это так, как только могу, без повторной реализации моей собственной структуры данных (излишнее, поскольку мой код сглаживания состоит всего из нескольких строк).

Спасибо за советы, друзья!


person Zhro    schedule 20.10.2012    source источник
comment
Знаете ли вы второй размер, я имею в виду, что размер всех элементов vector<int> должен быть одинаковым?   -  person PiotrNycz    schedule 21.10.2012


Ответы (4)


Буфер vector обычно распределяется динамически.

Это означает, что когда у вас есть vector из vectors, то в отношении внутренних буферов у вас есть что-то вроде массива указателей, каждый указатель указывает на массив.

Если вам нужно переключаться между 1D- и 2D-представлениями одних и тех же данных, проще всего, вероятно, просто определить класс 2D-массива, например этот (с ходу):

typedef ptrdiff_t Size;
typedef Size Index;

template< class Item >
class Array2D
{
private:
    std::vector< Item > items_;
    Size                width_;
    Size                height_;

    Index indexFor( Index const x, Index const y )
    { return y*width_ + x; }

public:
    Size width() const { return width_; }
    Size height() const { return height_; }

    Item& operator()( Index const x, Index const y )
    { return items_[indexFor( x, y )]; }

    Item const& operator()( Index const x, Index const y ) const
    { return items_[indexFor( x, y )]M; }

    Size bufferSize() const { return width_*height_; }
    Item* buffer() { return &items_[0]; }
    Item const* buffer() const { return &items_[0]; }

    Array2D( Size const w, Size const h )
        : items_( w*h )
        , width_( w )
        , height_( h )
    {}
};

Затем вы можете делать такие вещи, как

Array2D< int >  a( 4, 3 );

for( Index y = 0;  y < a.height();  ++y )
{
    for( Index x = 0;  x < a.width();  ++x )
    {
        foo( a( x, y ) );
    }
}

и

Array2D< int >  a( 4, 3 );

int* const pb = a.buffer();
for( Index i = 0;  i < a.bufferSize();  ++i )
{
    foo( pb[i];
}
person Cheers and hth. - Alf    schedule 20.10.2012
comment
+1: изменено. вы доминируете в этой колонке, поняли это в самом конце. совершенно правильно в любом случае. - person WhozCraig; 21.10.2012

Ну, вы не можете, потому что, хотя векторы гарантированно сохраняют свои элементы в непрерывной памяти, вектор векторов не гарантирует такую ​​​​же схему хранения, как двумерный массив в стиле C.

Так что нет, это невозможно, и я бы пошел еще дальше и сказал, что int* z = (int*)&v[0].front(); просто уродлив.

person Luchian Grigore    schedule 20.10.2012

Если ваши 1d-векторы (std::vector<int>) имеют одинаковый размер, сохраните векторные необработанные массивы:

typedef int ARRAY[6];
std::vector<ARRAY> yourVector; 

Если их размер не идентичен - тогда у вас реальная проблема, так как каждая std::vector<int> занимает непрерывную область памяти, а эти области не являются смежными. Точно так же, как в этом необработанном массиве C:

int** a = new int*[3];
a[0] = new int[4];
a[1] = new int[5];

a не указывает на непрерывную память размером (4+5)*sizeof(int)...

person PiotrNycz    schedule 20.10.2012

Теперь, как мне получить доступ к многомерному вектору как к плоскому многомерному массиву?

Вы не можете.

Причина, по которой вы можете получить доступ к vector<int> как к int[], заключается в том, что макет такой же. Схема памяти vector<vector<int>> отличается от схемы int*[], что означает, что вы не можете получить к ней доступ, как если бы это была int*[].

Если вы не можете избежать доступа к данным через int*[], то то, что вы делаете, выделяя массив int* и заполняя его указателями на векторы, - лучшее, что вы можете сделать. Было бы лучше, если бы вы могли избежать необходимости делать это в первую очередь.

Возможно, вы захотите использовать безопасный держатель исключений для файла int*[]. Вам также, вероятно, не нужно использовать vector::at(), и вы можете подумать, нужно ли вам обрабатывать случай, когда один из векторов пуст. Здесь я решаю эти проблемы, используя возможности C++11.

std::unique_ptr<int*[]> n(new int* [v.size()]);
transform(begin(v), end(v), n.get(), [](std::vector<int> &i) { return i.data(); });
person bames53    schedule 20.10.2012