2011-01-31 8 views
6

Mi piacerebbe usare un vettore std :: per controllare un dato pezzo di memoria. Prima di tutto sono abbastanza sicuro che questa non è una buona pratica, ma la curiosità ha la meglio su di me e mi piacerebbe sapere come farlo comunque.costrutto vettoriale C++ con memoria data

Il problema che ho è un metodo come questo:

vector<float> getRow(unsigned long rowIndex) 
{ 
    float* row = _m->getRow(rowIndex); // row is now a piece of memory (of a known size) that I control 
    vector<float> returnValue(row, row+_m->cols()); // construct a new vec from this data 
    delete [] row; // delete the original memory 
    return returnValue; // return the new vector 
} 

_m è una classe di interfaccia DLL che restituisce un array di float che è la responsabilità chiamanti da eliminare. Quindi mi piacerebbe racchiudere tutto ciò in un vettore e restituirlo all'utente .... ma questa implementazione alloca nuova memoria per il vettore, la copia, quindi elimina la memoria restituita, quindi restituisce il vettore.

Quello che mi piacerebbe fare è dire al nuovo vettore che ha il pieno controllo su questo blocco di memoria in modo che quando viene cancellato quella memoria viene ripulita.

UPDATE: La motivazione originale per questo (la memoria restituito da una DLL) è stato abbastanza saldamente schiacciato da un certo numero di responder :) Tuttavia, mi piacerebbe conoscere la risposta alla domanda è comunque ... esiste un modo per costruire un vettore std :: utilizzando un determinato blocco di memoria T * pre-allocata e la dimensione di questa memoria?

+1

Il codice pubblicato causa un comportamento non definito quando si chiama 'delete row' se la memoria è stata allocata con' new [] 'o' malloc' –

+0

Non penso che si possa semplicemente dare un 'vector' a un pezzo di memoria perché 'vector' deve essere in grado di allocare e deallocare a piacimento quando cambia la capacità del' vector'. In questa situazione, ciò richiede la conoscenza di come la DLL alloca/rilascia la memoria, che ovviamente non dovrebbe essere richiesta per lavorare con un'interfaccia DLL appropriata. –

+0

@ChrisDodd, errore di battitura è corretto. @Insilico Non mi dispiace se rilascia e rialloca questo ricordo ... Voglio solo poter dire "Inizia a usare questo pezzo di memoria" –

risposta

4

L'allocatore di default del vettore non fornisce questo tipo di accesso ai suoi interni. Potresti farlo con il tuo allocatore (il secondo parametro del modello del vettore), ma questo cambierebbe il tipo di vettore.

sarebbe molto più facile se si può scrivere direttamente nel vettore:

vector<float> getRow(unsigned long rowIndex) { 
    vector<float> row (_m->cols()); 
    _m->getRow(rowIndex, &row[0]); // writes _m->cols() values into &row[0] 
    return row; 
} 

Nota che & row [0] è un galleggiante * ed è garantito per il vettore per memorizzare gli elementi in modo contiguo.

+0

Idealmente mi piacerebbe farlo senza cambiare il tipo. Il punto è fornire al chiamante di getRow() un vettore standard (ma senza l'allocazione di memoria aggiuntiva) –

+1

+1 Se l'OP ha accesso alla sorgente DLL, questo è il modo migliore per farlo. –

+0

In questo caso, posso modificare la fonte della DLL (e molto probabilmente lo farò) tuttavia sono davvero interessato a sapere se è possibile farlo senza la modifica. –

0

La tua migliore scommessa è probabilmente una std::vector<shared_ptr<MatrixCelType>>.

Ulteriori dettagli in this thread.

+2

Dio no ...smettere di lanciare 'shared_ptr' ovunque per favore:/ –

4

La risposta ovvia è utilizzare un allocatore personalizzato, tuttavia si potrebbe trovare che sia davvero una soluzione abbastanza pesante per quello che ti serve. Se vuoi farlo, il modo più semplice è prendere l'allocatore definito (come argomento scond template predefinito per il vettore <>) dall'implementazione, copiarlo e farlo funzionare come richiesto.

Un'altra soluzione potrebbe essere quella di definire una specializzazione del modello del vettore, definire tanto dell'interfaccia quanto effettivamente necessario e implementare la personalizzazione della memoria.

Infine, come definire il proprio contenitore con un'interfaccia STL conforme, definendo iteratori di accesso casuale ecc. Questo potrebbe essere abbastanza facile dato che l'array sottostante si mapperà bene al vettore <> e i puntatori in esso verranno mappati agli iteratori.

Commento su UPDATE: "Esiste un modo per costruire un vettore std utilizzando un determinato blocco di memoria T * pre-allocata e la dimensione di questa memoria?"

Sicuramente la semplice risposta è "No". Se si desidera che il risultato sia un vettore <, allora deve supportare la crescita come richiesto, ad esempio tramite il metodo reserve(), e ciò non sarà possibile per una determinata allocazione fissa. Quindi la vera domanda è davvero: cosa vuoi raggiungere esattamente? Qualcosa che può essere usato come il vettore <>, o qualcosa che in un certo senso deve, in un certo senso, essere un vettore e, in tal caso, che senso ha?

0

Se si sta tentando di modificare dove/come il vettore alloca/rialloca/rilascia la memoria, il parametro del modello di allocatore della classe vettoriale è quello che si sta cercando.

Se stai semplicemente cercando di evitare il sovraccarico di costruzione, copia costruzione, assegnazione e distruzione, quindi consentire all'utente di creare un'istanza del vettore, quindi passarlo alla tua funzione per riferimento. L'utente è quindi responsabile della costruzione e della distruzione.

Sembra che quello che stai cercando sia una forma di puntatore intelligente. Uno che cancella ciò a cui punta quando viene distrutto. Cerca nelle librerie Boost o fai il rollover in quel caso.

2

La cosa più importante da sapere qui è che diversi DLL/moduli hanno diversi heap. Ciò significa che qualsiasi memoria allocata da una DLL deve essere eliminata da quella DLL (non è solo una questione di versione del compilatore o delete rispetto a delete[] o qualsiasi altra cosa). NON PASSARE LA RESPONSABILITÀ DELLA GESTIONE DELLA MEMORIA ATTRAVERSO UN LIMITE DI DLL. Ciò include la creazione di std::vector in una DLL e la sua restituzione. Ma include anche il passaggio di un std::vector alla DLL che deve essere riempita dalla DLL; un'operazione del genere non è sicura poiché non si è sicuri che lo std::vector non provi un ridimensionamento di qualche tipo mentre è pieno di valori.

ci sono due opzioni:

  • Definire il proprio allocator per la classe std::vector che utilizza una funzione di allocazione che è garantito per risiedere nella DLL/modulo da cui è stato creato il vettore. Questo può essere fatto facilmente con l'associazione dinamica (ovvero, fare in modo che la classe allocator chiami una funzione virtuale). Poiché il collegamento dinamico cercherà nel vtable per la chiamata di funzione, è garantito che cadrà nel codice dalla DLL/Modulo che lo ha originariamente creato.

  • Non passare l'oggetto vettoriale ao dalla DLL. È possibile utilizzare, ad esempio, una funzione getRowBegin() e getRowEnd() che restituiscono gli iteratori (cioè i puntatori) nell'array di riga (se è contiguo) e lasciano l'utente std::copy nel proprio oggetto locale std::vector. Si potrebbe anche farlo viceversa, passare gli iteratori begin() e end() a una funzione come fillRowInto(begin, end).

Questo problema è molto reale, anche se molte persone lo trascurano senza saperlo. Non sottovalutarlo. Ho sofferto personalmente bug silenziosi legati a questo problema e non era carino! Mi ci sono voluti mesi per risolverlo.

Ho verificato il codice sorgente e boost::shared_ptr e boost::shared_array utilizzano l'associazione dinamica (prima opzione sopra) per gestire questo .. tuttavia, non è garantito che siano binari compatibili. Tuttavia, questa potrebbe essere un'opzione leggermente migliore (in genere la compatibilità binaria è un problema molto minore rispetto alla gestione della memoria tra i moduli).

+0

"diverse DLL/Moduli hanno Heap diversi" - questo è vero solo se si modificano le impostazioni di generazione da quelle predefinite (o si costruisce con più compilatori, che è ancora più complesso). Con le impostazioni di default ('/ MD') c'è solo un heap. – MSalters

0

La libreria Boost.SmartPtr contiene un sacco di classi interessanti, alcune delle quali sono dedicate alla gestione degli array.

Per esempio, ecco scoped_array:

int main(int argc, char* argv[]) 
{ 
    boost::scoped_array<float> array(_m->getRow(atoi(argv[1]))); 
    return 0; 
} 

La questione, naturalmente, è che scoped_array non possono essere copiati, quindi se si vuole veramente un std::vector<float>, @Fred Nurk di è probabilmente la migliore che si può ottenere.

Nel caso ideale si vorrebbe l'equivalente a unique_ptr ma in forma di matrice, tuttavia non penso che sia parte dello standard.