Оборачивать динамический массив в контейнер STL / Boost? - PullRequest
8 голосов
/ 11 ноября 2009

Мне нужно обернуть динамически размещенный массив (например, из a = new double [100]) в std :: vector (желательно) без копирования массива. Это ограничение связано с тем, что массив, который я хочу обернуть, создается из файла, поэтому простое выполнение вектора (a, a + size) удвоит использование памяти.

Есть какие-нибудь хитрости, чтобы сделать это?

Ответы [ 8 ]

11 голосов
/ 11 ноября 2009

Одним из лучших решений для этого является что-то вроде шаблона array_proxy STLSoft <> . К сожалению, страница документа, сгенерированная из исходного кода doxygen, не сильно помогает пониманию шаблона. Исходный код может быть немного лучше:

Шаблон array_proxy<> хорошо описан в книге Мэтью Уилсона, Imperfect C ++ . Версия, которую я использовал, является урезанной версией того, что находится на сайте STLSoft, поэтому мне не нужно было тянуть всю библиотеку. Моя версия не так переносима, но это делает ее намного проще, чем на STLSoft (которая проходит через множество переносов).

Если вы установите переменную следующим образом:

int myArray[100];

array_proxy<int> myArrayProx( myArray);

Переменная myArrayProx имеет множество интерфейсов STL - begin(), end(), size(), итераторы и т. Д.

Так что во многих отношениях объект array_proxy<> ведет себя как вектор (хотя push_back() не существует, поскольку array_proxy<> не может расти - он не управляет памятью массива, он просто оборачивает его в чем-то немного ближе к вектору).

Одна очень хорошая вещь с array_proxy<> заключается в том, что если вы используете их в качестве типов параметров функции, функция может определить размер передаваемого массива, что не относится к собственным массивам. И размер обернутого массива не является частью типа шаблона, поэтому он довольно гибок в использовании.

9 голосов
/ 12 ноября 2009

A boost :: iterator_range предоставляет контейнероподобный интерфейс:

// Memory map an array of doubles:
size_t number_of_doubles_to_map = 100;
double* from_mmap = mmap_n_doubles(number_of_doubles_to_map);

// Wrap that in an iterator_range
typedef boost::iterator_range<double*> MappedDoubles;
MappedDoubles mapped(from_mmap, from_mmap + number_of_doubles_to_map);

// Use the range
MappedDoubles::iterator b = mapped.begin();
MappedDoubles::iterator e = mapped.end();
mapped[0] = 1.1;
double first = mapped(0);

if (mapped.empty()){
    std::cout << "empty";
}
else{
    std::cout << "We have " << mapped.size() << "elements. Here they are:\n"
       << mapped;
}
2 голосов
/ 11 ноября 2009

Когда-то я был полон решимости сделать то же самое. После нескольких дней размышлений и попыток я решил, что это того не стоит. Я закончил тем, что создал свой собственный вектор, который вел себя как std :: vector, но имел только те функциональные возможности, которые мне действительно нужны, такие как проверка границ, итераторы и т. Д.

Если вы все еще хотите использовать std :: vector, единственный способ, о котором я мог подумать тогда, - это создать собственный распределитель. Я никогда не писал, но, видя, что это единственный способ контролировать управление памятью в STL, может быть, есть что-то, что можно там сделать.

1 голос
/ 28 января 2011

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

1 голос
/ 12 ноября 2009

Вы можете перейти с array_proxy <> или взглянуть на Boost.Array . Он дает вам size (), front (), back (), at (), operator [] и т. Д. Лично я бы предпочел Boost.Array, так как Boost в любом случае более распространен.

1 голос
/ 11 ноября 2009

Как насчет вектора указателей, которые указывают на элементы отображаемой области (уменьшенное потребление памяти как sizeof (double *)

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

Существует общая проблема всех решений с использованием std :: vector в отображаемом файле: «привязать» векторное содержимое к отображаемой области. Это невозможно отследить, вы можете только следить за собой, чтобы не использовать что-то, что может привести к перераспределению векторного содержимого. Так что будьте осторожны в любом случае.

1 голос
/ 11 ноября 2009

Нет, это невозможно при использовании std::vector.

Но если возможно, вы можете создать вектор с таким размером и вместо этого сопоставить файл с ним.

std::vector<double> v(100);
mmapfile_double(&v[0], 100);
0 голосов
/ 28 февраля 2013

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

#include <vector>
#include <iostream>

template <class T>
void wrapArrayInVector( T *sourceArray, size_t arraySize, std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
    (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = sourceArray;
  vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = vectorPtr->_M_start + arraySize;
}

template <class T>
void releaseVectorWrapper( std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
        (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = NULL;
}

int main() {

  int tests[6] = { 1, 2, 3, 6, 5, 4 };
  std::vector<int> targetVector;
  wrapArrayInVector( tests, 6, targetVector);

  std::cout << std::hex << &tests[0] << ": " << std::dec
            << tests[1] << " " << tests[3] << " " << tests[5] << std::endl;

  std::cout << std::hex << &targetVector[0] << ": " << std::dec
            << targetVector[1] << " " << targetVector[3] << " " << targetVector[5] << std::endl;

  releaseVectorWrapper( targetVector );
}

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

template <class T>
class vectorWrapper : public std::vector<T>
{   
public:
  vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  vectorWrapper(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   

  ~vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  void wrapArray(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   
};  
...