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

Как мне добиться следующего:

std::vector<int> vec = { 1, 2, 3 };
const int N = vec.size();
// Now create NxN 2D array.

Во-первых, я знаю, что могу сделать это с new, но мне придется не забыть удалить его позже, и я бы предпочел не обрабатывать освобождение памяти, если это возможно.

Во-вторых, я не могу объявить 2D-массив в стеке, потому что N не является (и не может быть в данном случае) постоянным выражением. (В любом случае я использую VS2013, и он не поддерживает constexpr.)

В-третьих, я не могу (или, может быть, не знаю, как) использовать std::array, потому что, очевидно, «локальная переменная не может использоваться как аргумент, не являющийся типом». (Я скопировал это из диалога компиляции VS2013 и мало что понимаю по этому поводу).

В-четвертых, я подумываю использовать unique_ptr. Проблема в том, что я знаю, как использовать unique_ptr для одномерного массива, например std::unique_ptr<int> arr{ new int[N] }, но не могу понять, как это сделать для 2D-массива.

Наконец, я знаю, что всегда могу написать свою собственную тонкую оболочку вокруг массива в стиле C, который всегда создается в куче, или написать свой собственный класс 2D-массива. Но есть ли в C ++ (C ++ 11) собственный или стандартный библиотечный способ сделать это?


person Ray    schedule 06.04.2015    source источник
comment
Я предлагаю написать класс 2D-массива с одним вектором в качестве хранилища данных. Вы можете предоставить int& operator()(size_t row, size_t col); для доступа к 2-му элементу.   -  person juanchopanza    schedule 06.04.2015
comment
@Igor Я бы предпочел 2D-массив, а не 2D-вектор.   -  person Ray    schedule 06.04.2015
comment
I'd rather have a 2D array rather than a 2D vector. Почему? Есть ли у вас дополнительные требования, которые вы не упомянули, которым вектор векторов не удовлетворяет?   -  person Igor Tandetnik    schedule 06.04.2015
comment
Хм, я полагаю, вы правы, просто я пока только представлял себе вариант использования моей проблемы в терминах массивов (потому что мне не нужно менять размер, и я точно знаю, сколько места выделить из начало). Я полагаю, это могло бы работать с std::vectors. Мне все еще интересно посмотреть, есть ли простое решение с массивами, хотя так я узнаю больше о C ++.   -  person Ray    schedule 06.04.2015
comment
@Ray Если вам нужно, чтобы данные были смежными, вектор векторов не годится. Помимо косвенного обращения, есть также накладные расходы на размер для каждого вектора. Об этом стоит помнить, потому что это может иметь значение в зависимости от варианта использования.   -  person juanchopanza    schedule 06.04.2015
comment
Просто FWIW, оболочка 2D-векторов.   -  person Jerry Coffin    schedule 06.04.2015
comment
@juanchopanza, что касается вашего первого комментария, я попытался сделать operator[](size_t i, size_t j), но он не работает, потому что он не определяет этот оператор или преобразование в тип, приемлемый для предопределенного оператора. Я не совсем понимаю это, но есть ли способ заставить индексацию работать с [] вместо ()?   -  person Ray    schedule 06.04.2015
comment
@Ray operator[] может принимать только один индекс, поэтому вы застряли с operator()(size_t row, size_t col);. Думаю, это нормально.   -  person juanchopanza    schedule 06.04.2015
comment
Но есть ли способ сделать это в C ++ (C ++ 11) с помощью нативной или стандартной библиотеки? К сожалению, нет. Написание собственного класса 2-мерного массива (или использование того, который кто-то ранее вставил в библиотеку) - это правильный поступок.   -  person bames53    schedule 08.04.2015
comment
@juanchopanza Это правда, что [] принимает только один аргумент, но вы все равно можете его использовать. В моем ответе здесь показан пример. Я также использую это в более сложном доказательстве концепции.   -  person bames53    schedule 08.04.2015


Ответы (4)


std::experimental::array_view - это взгляд на -мерный массив с динамическими границами размера упакованного буфера.

Итак, один из подходов - создать непрерывный буфер (скажем, std::vector<T> или std::unique_ptr<T[]>, а затем обернуть его array_view<T,2>.

Доступ через объект представления, и он будет иметь операции, которые вы должны ожидать от массива. Хранилище управляется отдельно от способа просмотра хранилища.

Написать упрощенную версию этого array_view для случая 1 и 2 не сложно. Но в результате ваш код отличается высокой производительностью и очень понятным при использовании. Связующий код (для array_view) может быть немного сложным, но после тестирования он должен быть прочным: и вероятность того, что аналогичная конструкция будет добавлена ​​в std в ближайшее время, означает, что он не останется неясным надолго.

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

Если вы хотите написать свой собственный, я бы пропустил биты о границах и индексах и просто реализовал срезку - [] по 2-му измерению array_view возвращает 1-е измерение array_view, а [] по 1-му измерению array_view возвращает T&.

person Yakk - Adam Nevraumont    schedule 06.04.2015
comment
Я понятия не имею о array_view, но проверю. Спасибо. - person Ray; 07.04.2015

Предлагаю вам написать для этого класс.

Пример ниже: set () изменяет его размер перед установкой значений. Оператор [] возвращает вектор-столбец для этой строки, поэтому, когда вы применяете оператор [], он возвращает желаемое значение. Дайте мне знать, если вы обнаружите какие-либо проблемы;).

class 2DVector {
 std::vector<std::vector<int>> m_items;

 void set(int value, size_t row, size_t column) {
   for (int i=m_items.size(); i<=row; i++) {
      m_items.push_back(std::vector<int>());
   }
   for (int i=0; i<m_items.size(); i++) {
      for (int j=m_items[i].size(); j<=column; j++) {
      m_items[i].push_back(0);
   }
   m_items[row][column] = value;
 }

 std::vector<int> &operator [](size_t index) {
   return m_items[index];
 }
}

Использование:

2DVector v;
v.set(200, 0, 0);
v.set(201, 1, 0);
std::cout << v[0][0]; //prints 200
std::cout << v[1][0]; //prints 201
person Pedro Israel    schedule 06.04.2015
comment
Это создает зубчатый массив (массив несвязанных массивов), что часто является плохой идеей по соображениям производительности. - person Yakk - Adam Nevraumont; 06.04.2015
comment
Согласитесь @Yakk. Всегда существует компромисс между удобством использования / читабельностью и производительностью. Это решение, которое Рэй должен будет принять и соответственно изменить реализацию и интерфейс класса. - person Pedro Israel; 06.04.2015
comment
Единственная причина использовать vector<vector<int>> состоит в том, что вы можете просто использовать его напрямую, не определяя ничего другого. Поскольку вы все равно заключаете тип в класс, я бы сказал, просто сделайте это вправо; При правильном выполнении класс становится не менее удобным и читаемым. - person bames53; 08.04.2015

Стандартный библиотечный способ сделать это:

std::vector< std::vector<int> > vec2d (vec.size(), vec);

который инициализирует каждую строку значениями из vec. Если вы не хотите этого делать, оставьте последний аргумент.

person M.M    schedule 08.04.2015

А как насчет имитации двумерного массива одномерным массивом? Как в openCV2

псевдокоды

class 2DVector {         
 void set(int value, size_t row, size_t column) {
   m_items[row * column_size + column];
 }

 int &operator [](size_t row, size_t column) {
   return m_items[row * column_size + column];
 }

 private:
   std::vector<int> m_items;
}

Или просто используйте boost :: multi_array (не уверен, что производительность достаточно хороша для вашего случая).

person user3689849    schedule 07.04.2015
comment
Я думаю, int& operator [](size_t row, size_t column) не работает. См. Последнюю пару комментариев к моему вопросу. Однако мне нравится идея имитировать 2D-массив 1D-массивом. - person Ray; 07.04.2015
comment
Да, у вас не может быть двух аргументов [], подобных этому (к сожалению), но вы можете сделать это, как показано в этот пример, который почти так же хорош. - person bames53; 08.04.2015