2009-11-11 6 views
8

Ho bisogno di avvolgere un array allocato dinamicamente (da a = new double [100] per esempio) in std :: vector (preferibilmente) senza copiare l'array. Questa restrizione è imposta dal fatto che l'array che voglio avvolgere viene rimosso da un file, quindi il solo vettore (a, una dimensione +) raddoppierà l'utilizzo della memoria.Disposizione dell'array dinamico nel contenitore STL/Boost?

È qualche trucco per farlo?

+1

In ogni caso non sarà possibile ottenere tutta una serie di funzionalità di std :: vector (ad esempio, ridimensionare, probabilmente non funzionerà), quindi potrebbe essere utile elencare di cosa hai bisogno esattamente dal wrapper? – maxim1000

risposta

11

Una delle migliori soluzioni per questo è qualcosa come il modello STLSoft's array_proxy<>. Sfortunatamente, la pagina doc generata dal codice sorgente da doxygen non aiuta molto a capire il modello. Il codice sorgente potrebbe effettivamente essere un po 'meglio:

Il modello array_proxy<> è descritto bene in Matthew Wilson's book, Imperfect C++. La versione che ho usato è una versione ridotta di ciò che si trova sul sito STLSoft, quindi non ho dovuto richiamare l'intera libreria. La mia versione non è così portatile, ma ciò lo rende molto più semplice di ciò che è su STLSoft (che salta attraverso un sacco di circuiti portabili).

Se si imposta una variabile in questo modo:

int myArray[100]; 

array_proxy<int> myArrayProx(myArray); 

La variabile myArrayProx ha molte delle interfacce STL - begin(), end(), size(), iteratori, ecc

Così in molti modi, l'oggetto array_proxy<> si comporta proprio come un vettore (anche se push_back() non è lì dal array_proxy<> non può crescere - non di gestire la memoria dell'array, semplicemente avvolge in qualcosa di un po 'più vicino a un vettore).

Una cosa veramente bella con array_proxy<> è che se li si usano come tipi di parametri di funzione, la funzione può determinare la dimensione della matrice passata, il che non è vero per gli array nativi. E la dimensione dell'array avvolto non fa parte del tipo del modello, quindi è abbastanza flessibile da usare.

+1

+1, sembra essere la soluzione migliore, soprattutto perché il proxy non dovrebbe riallocare la memoria, quindi dovrebbe essere libero da molti problemi. –

1

No, questo non è possibile utilizzando std::vector.

Ma se possibile, è possibile creare il vettore con questa dimensione, e possibile mappare il file su quello.

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

Che dire di vettore di puntatori che puntano al tuo elementi zona mappati (ridotto consumo di memoria come sizeof (double *) < sizeof (double))? Per te va bene?

Vi sono alcuni svantaggi (primari sono necessari predicati speciali per l'ordinamento) ma anche alcuni vantaggi, ad esempio, è possibile eliminare elementi senza modificare il contenuto mappato effettivo (o disporre di un numero pari di tali matrici con un diverso ordine di elementi senza qualsiasi modifica ai valori effettivi).

C'è un problema comune a tutte le soluzioni con std :: vector su file mappato: per "inchiodare" il contenuto vettoriale all'area mappata. Questo non può essere tracciato, puoi solo guardare dopo te stesso per non usare qualcosa che potrebbe portare alla ridistribuzione del contenuto vettoriale. Quindi stai attento in ogni caso.

2

Una volta ero determinato a realizzare esattamente la stessa cosa. Dopo alcuni giorni di pensieri e tentativi, ho deciso che non ne valeva la pena. Ho finito per creare il mio vettore personalizzato che si comportava come std :: vector ma aveva solo la funzionalità che avevo effettivamente bisogno di controllo, iteratori, ecc.

Se si desidera ancora usare std :: vector, l'unico modo che potrei pensare a quel tempo era creare un allocatore personalizzato. Non ne ho mai scritto uno, ma visto che questo è l'unico modo per controllare la gestione della memoria di STL forse c'è qualcosa che può essere fatto lì.

+2

Suggerisco di non scrivere un contenitore personale. La maggior parte delle volte semplicemente non ne vale la pena e porta a diversi problemi di interoperabilità. Inoltre, il tempo speso per il debug di materiale reinventato è tempo perso. L'allocatore personalizzato tuttavia è probabilmente abbastanza facile da implementare e un approccio migliore. – MP24

+0

Normalmente sarei d'accordo, ma penso davvero che dipenda da cosa farai. La maggior parte delle operazioni "complesse" in std :: vector probabilmente hanno qualcosa a che fare con la gestione della memoria e succede che in questo caso nessuno di loro è richiesto. Quindi scrivere una piccola classe con controllo dei limiti e un puntatore come iteratore non dovrebbe richiedere troppo tempo. Non posso commentare l'interoperabilità poiché non ho conoscenza di dove/come l'autore intende usarlo. Ma il supporto iteratore dovrebbe farlo funzionare con gli algoritmi di STL. –

+0

Quindi, hai un collegamento al codice per questo? – einpoklum

1

Si potrebbe andare con array_proxy <>, o dare un'occhiata a Boost.Array. Ti dà size(), front(), back(), at(), operator [], ecc. Personalmente, preferirei Boost.Array poiché Boost è più diffuso comunque.

+0

-1, Boost.Array copia il contenuto dell'array – CharlesB

9

Un boost::iterator_range fornisce un'interfaccia a contenitore:

// 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

La migliore risposta per me, poiché utilizza boost! – CharlesB

1

bene, il modello di vettore permette di fornire il proprio allocatore di memoria. Non l'ho mai fatto da solo, ma immagino che non sia così difficile farlo puntare al tuo array, magari con il posizionamento di un nuovo operatore ... è solo un'ipotesi, scrivo di più se provo e riesco.

0

Ecco la soluzione alla tua domanda. Avevo tentato questo e quello per un po 'di tempo prima di trovare una soluzione praticabile. L'avvertenza è che è necessario azzerare i puntatori dopo l'uso al fine di evitare il doppio liberare la memoria.

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

In alternativa si può solo fare una classe che eredita da vettoriale e nulli i puntatori su di distruzione:

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