2013-07-12 1 views
2

Так что я недавно познакомился (и влюбился) в boost и C++ 11 умных указателей. Это значительно облегчает управление памятью. И, помимо всего прочего, они обычно могут работать с устаревшим кодом (с помощью вызова get)Динамически распределенные массивы с янтарными указателями

Однако большая дыра, в которой я продолжаю работать, - это многомерные зубчатые массивы. Правильный способ сделать это - иметь boost::scoped_array<boost::scoped_array<double>> или vector<vector<double>>, который будет хорошо очищаться. Однако вы не можете легко получить double**, чтобы отправить устаревший код.

Есть ли способ сделать это, или я застрял с не-умными зубчатыми массивами?

+0

Что такое API вашего «устаревшего кода»? Я не вижу, как вы могли бы передать зубчатый массив как 'double **' - где информация о размере? – Casey

ответ

0

Я хотел бы начать с std::vector<std::vector<double>> для хранения , если структура не была очень статичной.

Для того, чтобы произвести мой массив из-массивов, я бы производить std::vector<double*> с помощью трансформации моего хранения выше, с использованием синтаксиса, как transform_to_vector(storage, [](std::vector<double>& v) { return v.data(); }) (transform_to_vector в качестве упражнения для читателя).

Сохранение двух в синхронизации было бы простым делом обернуть его в небольшом классе.

Если зубчатая массив относительно фиксированного размера, я бы взял std::vector<std::size_t> создать свой буфер (или, может быть, std::initializer_list<std::size_t> - на самом деле, а template<typename Container>, и я только for(:) над ним дважды, и пусть вызывающий выберите, какой контейнер он мне предоставил), затем создайте один std::vector<double> с суммой размеров, затем создайте std::vector<double*> при прописанных смещениях.

Изменение размера становится дорогим, что является недостатком.

Хорошим свойством использования std::vector является то, что новые API имеют полный доступ к красивым begin и end значениям. Если у вас есть один большой буфер, вы можете открыть представление диапазона подматриц для нового кода (структура, содержащая double* begin() и double* end(), и пока мы находимся у нее double& operator[] и std::size_t size() const { return end()-begin(); }), поэтому они могут погреться во славе полный на представлениях контейнера в C++, сохраняя совместимость C для устаревших интерфейсов.

0

Если вы работаете в C++ 11, вам, вероятно, следует работать с unique_ptr<T[]>, а не с scoped_array<T>. Он может делать все, что может scoped_array, а затем и некоторые.

Если вы хотите прямоугольный массив, я рекомендую использовать unique_ptr<double[]> для хранения основных данных и unique_ptr<double*[]> для хранения баз строк. Это будет работать что-то вроде этого:

unique_ptr<double[]> data{ new double[5*3] }; 
unique_ptr<double*[]> rows{ new double*[3] }; 
rows[0] = data.get(); 
for (size_t i = 1; i!=5; ++i) 
    rows[i] = rows[i-1]+3; 

Затем вы можете передать rows.get() на функцию, принимающую double**. Этот подход может работать и для непрямоугольного массива, если геометрия массива известна во время создания массива, так что вы можете сразу распределить все данные и указать rows на правильные смещения. (Это может быть не так просто, как простой цикл).

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

Если вы хотите изменить геометрию массива с зубчатым колесом после его создания, вам необходимо разработать принципиальный способ управления хранилищем для этого решения. Однако, поскольку изменение геометрии с использованием scoped_array неудобно (требует специального использования swap()), я не удивлюсь, если это не проблема для вас.

(Обратите внимание, что этот подход может работать с scoped_array, а также unique_ptr<[]>, я просто иллюстрирующим его с помощью unique_ptr, так как мы в C++ 11 в настоящее время.)