2009-11-11 13 views
8

Potrzebuję zawinąć dynamicznie przydzieloną tablicę (od a = new double [100] na przykład) do std :: vector (najlepiej) bez kopiowania tablicy. To ograniczenie jest narzucone przez to, że tablica, którą chcę opakować, jest zmapowana z pliku, więc samo wykonanie wektora (a, a + rozmiar) podwoi użycie pamięci.Owijanie tablicy dynamicznej w pojemnik STL/Boost?

Czy można to wykorzystać?

+1

W każdym razie nie dostaniesz cały zestaw funkcji std :: vector (np zmiana rozmiaru, najprawdopodobniej nie zadziała), więc może dobrze byłoby wymienić to, czego dokładnie potrzebujesz z opakowania? – maxim1000

Odpowiedz

11

Jednym z najlepszych rozwiązań jest szablon podobny do STLSoft's array_proxy<>. Niestety strona doc wygenerowana z kodu źródłowego przez doxygen nie pomaga w zrozumieniu szablonu. Kod źródłowy może być w rzeczywistości nieco lepiej:

Szablon array_proxy<> opisany jest ładnie w Matthew Wilson's book, Imperfect C++. Używana przeze mnie wersja to uproszczona wersja tego, co jest na stronie STLSoft, więc nie musiałem ściągać całej biblioteki. Moja wersja nie jest tak przenośna, ale dzięki temu jest o wiele prostsza niż na STLSoft (która przeskakuje przez mnóstwo przenośnych obręczy).

Jeśli ustawisz zmienną tak:

int myArray[100]; 

array_proxy<int> myArrayProx(myArray); 

Zmienna myArrayProx ma wiele interfejsów STL - begin(), end(), size(), iteratory, etc.

więc na wiele sposobów, obiekt array_proxy<> zachowuje się podobnie jak wektor (choć push_back() nie jest tam od array_proxy<> nie może się rozwijać - nie zarządzać pamięci tablicy jest, to po prostu zawija je w coś trochę bliżej do wektora).

Jedną z naprawdę fajnych rzeczy z array_proxy<> jest to, że jeśli używasz ich jako typów parametrów funkcji, funkcja może określić rozmiar przekazanej tablicy, co nie jest prawdą w przypadku tablic natywnych. Rozmiar zawiniętej tablicy nie jest częścią szablonu, więc jest dość elastyczny w użyciu.

+1

+1, wydaje się być najlepszym rozwiązaniem, zwłaszcza, że ​​proxy nie powinno przenosić pamięci, więc powinno być wolne od wielu problemów. –

1

Nie, nie jest to możliwe przy użyciu std::vector.

Ale jeśli to możliwe, możesz utworzyć wektor o tym rozmiarze, a zamiast tego można go przypisać do pliku.

std::vector<double> v(100); 
mmapfile_double(&v[0], 100); 
1

Co wektora wskaźników wskazujących na swoim odwzorowanych elementów strefy (mniejsze zużycie pamięci jako sizeof (double *) < sizeof (double))? Zgadzasz się z tym?

Istnieje kilka wad (podstawowe jest to, że potrzebujesz specjalnych predykatów do sortowania), ale niektóre zalety, jak możesz, na przykład, usuń elementy bez zmiany rzeczywistej zamapowanej zawartości (lub mają nawet liczbę takich tablic o innej kolejności elementów bez dowolna zmiana wartości rzeczywistych).

Powszechnie występuje problem wszystkich rozwiązań z std :: vector na zmapowanym pliku: do "wbijania" zawartości wektorowej w zmapowany obszar. Nie można tego śledzić, można tylko oglądać po sobie, aby nie używać czegoś, co może doprowadzić do ponownego przydziału treści wektorowych. W każdym razie bądź ostrożny.

2

Byłem zdeterminowany, aby osiągnąć dokładnie to samo. Po kilku dniach myślenia i próbowania zdecydowałem, że nie było tego warte. Skończyło się na tym, że stworzyłem własny wektor, który zachowywał się jak std :: vector, ale miał tylko tę funkcję, której potrzebowałem, jak sprawdzanie, iteratory itp.

Jeśli nadal chcesz używać std :: vector, jedyny sposób, w jaki mógłbym to zrobić myśleć wtedy o stworzeniu niestandardowego przydziału. Nigdy nie napisałem, ale widząc, że jest to jedyny sposób kontrolowania zarządzania pamięcią STL, być może jest coś, co można tam zrobić.

+2

Sugeruję, aby nie pisać własnego pojemnika. Zazwyczaj po prostu nie warto i prowadzi do wielu problemów z interoperacyjnością. Dodatkowo czas spędzony na debugowaniu nowo odkrytych rzeczy to strata czasu. Przydzielenie niestandardowego przydziału jest jednak prawdopodobnie dość łatwe do wdrożenia i lepszego podejścia. – MP24

+0

Normalnie zgodziłbym się, ale naprawdę myślę, że to zależy od tego, co zamierzasz z tym zrobić. Większość "złożonych" operacji w std :: vector prawdopodobnie ma coś wspólnego z zarządzaniem pamięcią i zdarza się, że w tym przypadku żaden z nich nie jest wymagany. Zatem pisanie małej klasy z kontrolą granic i wskaźnikiem jako iteratorem nie powinno trwać zbyt długo. Nie mogę wypowiedzieć się na temat interoperacyjności, ponieważ nie wiem, gdzie/jak autor zamierza z niego korzystać. Jednak wsparcie iteracyjne powinno działać z algorytmami STL. –

+0

Masz link do kodu? – einpoklum

1

Możesz użyć array_proxy <> lub spojrzeć na Boost.Array. Daje ci size(), front(), back(), at(), operator [], itp. Osobiście wolałbym Boost.Array, ponieważ Boost jest bardziej powszechny.

+0

-1, Boost.Array kopiuje zawartość tablicy – CharlesB

9

boost::iterator_range zapewnia interfejs podobny do kontenera:

// 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; 
} 
+1

Najlepsza odpowiedź dla mnie, ponieważ wykorzystuje boost! – CharlesB

1

dobrze, szablon pozwala zapewnić własne przydzielania pamięci. Nigdy tego nie robiłem, ale myślę, że nie jest to takie trudne, aby wskazywać na twoją tablicę, może z nowym operatorem umieszczenia ... po prostu przypuszczam, piszę więcej, jeśli spróbuję i odniosę sukces.

0

Oto rozwiązanie Twojego pytania. Próbowałem tego od czasu do czasu, zanim wymyśliłem praktyczne rozwiązanie. Ograniczeniem jest to, że musisz wyzerować wskaźniki po użyciu, aby uniknąć podwójnego uwolnienia pamięci.

#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); 
} 

Alternatywnie można po prostu zrobić klasę, która dziedziczy z wektorem i null się ze wskazówek na zniszczenia:

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; 
    } 
}; 
Powiązane problemy