std :: вектор и непрерывная память многомерных массивов
Я знаю, что стандарт не заставляет std::vector
выделять смежные блоки памяти, но все реализации все равно подчиняются этому.
Предположим, что я хочу создать вектор многомерного статического массива. Рассмотрим 2 измерения для простоты и вектор длины N. Это я хочу создать вектор с N элементами, например, int[5]
.
Могу ли я быть уверенным, что все N * 5 целые числа теперь смежны в памяти? Чтобы я в принципе мог получить доступ ко всем целым, просто зная адрес первого элемента? Является ли эта реализация зависимой?
- Что каждый программист должен знать о памяти?
- Можно ли программно определить размер массива C ++? А если нет, то почему?
- Как узнать, указывает ли указатель на кучу или стек?
- Методы classа, которые создают новые экземпляры
- Предупреждения памяти iPhone OS. Что означают разные уровни?
Для справки, как я в настоящее время создаю 2D-массив в смежном блоке памяти, сначала создаем (динамический) массив float * длины N, выделяя все N * 5 поплавков в одном массиве и затем копируя адрес каждого 5-го элемента в первый массив float*
.
- Динамическое распределение массива объектов
- Проверьте, указывает ли указатель на выделенную память в куче
- Чтение больших файлов в Java
- Размер строки кешей L1 и L2
- Размещение массива-new требует неопределенных накладных расходов в буфере?
- Как работает механизм сбора мусора?
- Почему моя программа работает медленнее, если вы перебираете ровно 8192 элементов?
- Мусорный коллектор MATLAB?
Для справки, как я в настоящее время создаю 2D-массив в смежном блоке памяти, сначала создаем (динамический) массив float * длины N, выделяя все N * 5 поплавков в одном массиве и затем копируя адрес каждого 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;
Обратите внимание, что существует только одно выделение. Могу ли я предложить больше узнать об использовании массивов на C ++ ?
Стандарт требует, чтобы память std::vector
была смежной. С другой стороны, если вы пишете что-то вроде:
std::vector > v;
глобальная память (все v[i][j]
) не будет смежной. Обычным способом создания 2D-массивов является использование одного
std::vector v;
и рассчитывать индексы, точно так же, как вы предлагаете делать с float
. (Вы также можете создать второй std::vector
с адресами, если хотите. Я всегда просто пересчитывал индексы.)
Элементы вектора гарантируют непрерывность в соответствии со стандартом C ++.
Котировки из стандарта заключаются в следующем:
Из n2798 (черновик C ++ 0x):
23.2.6 Вектор шаблона шаблона [вектор]
1 Вектор представляет собой контейнер последовательности, который поддерживает iteratorы с произвольным доступом. Кроме того, он поддерживает (амортизируется) постоянное время вставки и стирания операций в конце; вставлять и стирать в середине, принимать линейное время. Управление хранилищем обрабатывается автоматически, хотя можно дать подсказки для повышения эффективности. Элементы вектора сохраняются смежно, что означает, что если v – вектор, где T – некоторый тип, отличный от bool, то он подчиняется идентификатору & v [n] == & v [0] + n для всех 0 <= n
Стандарт C ++ 03 (23.2.4.1):
Элементы вектора сохраняются смежно, что означает, что если v – вектор, где T – некоторый тип, отличный от bool, то он подчиняется идентификатору & v [n] == & v [0] + n для всех 0 <= n
Кроме того, смотрите здесь то, что взгляды Херба Саттера на то же самое.
Как уже указывал @Als, да, std::vector
(теперь) гарантирует непрерывное распределение. Однако я бы не смоделировал 2D-матрицу с массивом указателей. Вместо этого я бы рекомендовал один из двух подходов. Чем проще (безусловно) использовать operator()
для подписи и делать умножение для преобразования двумерного ввода в линейный адрес в векторе:
template class matrix2D { std::vector 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]
адресацию стиля, вы можете использовать прокси-class для обработки преобразования. Хотя это было для 3D-матрицы вместо 2D, я опубликовал демонстрацию этой техники в предыдущем ответе .
Под капотом вектор может выглядеть примерно как (p-код):
class vector { T *data; size_t s; };
Теперь, если вы создадите vector
, будет такой макет, как этот
vector> --> data { vector, vector , vector };
или в «вложенной» форме
vector> --> data { {data0, s0}, {data1, s1}, {data2, s2} };
Да, вектор-вектор поэтому использует непрерывную память, но нет, не так, как вам бы хотелось. Скорее всего, он хранит массив указателей (и некоторых других переменных) во внешних местах.
Стандарт требует только того, чтобы данные вектора были смежными, но не вектором в целом.
Простой class для создания, как вы его называете, 2D-массива , будет выглядеть примерно так:
template 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 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]
наивным ожиданиям в том, что это myarray[y][x]
.
В общем, если вы взаимодействуете с кодом, который требует своего рода classического плоского массива C-style, то почему бы просто не использовать его?
Изменить: Как сказано выше, все просто . Никакие ограничения не проверяют попытки вообще. Также как «массив».