2010-10-16 10 views
5

Si supponga di disporre di un metodo C++ che restituisce un puntatore a un oggetto. Qualcosa nel file di intestazione che assomiglia a questo:Convenzione di nomi per l'eliminazione dei dati in C++

uint8_t* getData(void); 

Questo ragazzo restituisce un array di byte, ma non c'è nulla che dice che se questo è un pezzo dinamico o generato staticamente dei dati (locale alla classe o creati con il nuovo).

Esiste una convenzione di denominazione specifica in C++ per distinguere tra metodi che restituiscono memoria allocata dinamicamente (e quindi deve essere cancellata dal richiedente), vs metodi che restituiscono semplicemente un riferimento a una porzione di dati definita staticamente?

Qual è il modo preferito per farlo o dipende dalla situazione?

+0

"deve essere eliminato dal richiedente" -> Codice errato. Non cancellare nulla manualmente, avvolgerlo. – GManNickG

risposta

2

mie convenzioni sono:

uint8_t* getData(); 

è assegnato staticamente, o almeno non è la mia responsabilità per l'eliminazione di questi dati. Tuttavia, se si tratta di un array, scriverei:

pair<uint8_t*,uint8_t*> getData(); 

Oppure definire un contenitore per quello.

auto_ptr<uint8_t> getData(); 
unique_ptr<uint8_t> getData(); 

alloca singolo oggetto e ora lo possiedo.

shared_ptr<uint8_t> getData(); 

alloca singolo oggetto con proprietà condivisa.

vector<uint8_t> getData(); 

alloca una matrice, vettore possiede la memoria.

+1

Buona idea, tranne chi implementi l'ultima opzione senza una copia? Userei 'getData (vector & ouput)'. –

+2

@larsmans: Scrivo questo solo se è davvero un problema di prestazioni, per tre ragioni: 1) è un'ottimizzazione, quello che intendo dire è che * return * a vector 2) il compilatore può ottimizzarlo in molti casi tramite copia elision 3) ci stiamo spostando verso C++ 0x con riferimenti rvalue, evviva! – ybungalobill

+0

Ah! Meglio andare a leggere la bozza C++ 0x. Grazie! –

3

In C++, in primo luogo non distribuite semplicemente i puntatori grezzi ai client. Una possibile soluzione:

std::vector<uint8_t> getData(); 
+1

non che non sia possibile, è solo una cattiva idea (per i motivi citati nella domanda) e ci sono alternative – stefaanv

+1

@stefaanv: è una buona idea, leggi i commenti alla mia risposta. – ybungalobill

+0

@ybungalobill: Presumo che tu abbia interpretato male il mio commento: è una brutta idea distribuire dei puntatori grezzi. Ho svalutato questa risposta. Ma ammetto che il mio commento è stato troppo breve. – stefaanv

0

Inserire nella documentazione.

+0

+1 a meno che non sia ovvio, la proprietà (ad esempio chi è responsabile della pulizia) dovrebbe sempre essere documentata; in questo esempio, ovviamente NON è ovvio MrGreen – franji1

+1

Consiglio peggiore di sempre. –

+0

@ Matthieu Sì, hai ragione. Non dovremmo documentare il nostro codice. –

2

MODIFICATO!
Se si restituisce il puntatore a una matrice allocata localmente, il programma si bloccherà quando si tenta di utilizzare questo puntatore, poiché dopo essere usciti dalla funzione, la matrice verrà distrutta. In realtà, un crash sarà il comportamento migliore, perché questa situazione è un comportamento indefinito. Ecco perché - restituisci un indirizzo di qualcosa che non esiste più. Quindi, il miglior pensiero che potrebbe accadere è un crash (dopo aver provato ad usarlo comunque). L'altra opzione è che questo indirizzo sia un indirizzo di un altro oggetto nel tuo programma (non sai quale), quindi se provi a modificarlo, è davvero una brutta cosa.
L'altra variante - restituire il puntatore all'array, assegnata con "new" è molto pericolosa ed è una potenziale perdita di memoria . Quindi, usa i puntatori intelligenti: è il modo migliore (/ * vedi il commento di Roger Pate a questa domanda sull'uso di puntatori intelligenti */
L'altro modo è di restituire una copia dell'array (usando std :: vector), ma è molto lento (se la matrice è grande, specialmente quando la matrice è di un grande tipo definito dall'utente), e nella maggior parte dei casi non è una buona idea.

// Grazie a ybungalobill, Steve e Roger Patè per i loro commenti!

+0

Qualcuno può dirmi per cosa è questo -1? Voglio dire, voglio sapere il mio errore –

+0

Anche se non è il mio voto, suppongo che sia per "se si restituisce il puntatore a un array assegnato statico il programma si bloccherà". – ybungalobill

+0

Ci ho pensato, ma è vero. È completamente possibile creare array statici e restituire il puntatore ad esso. Come ho detto, il programma si bloccherà (o è un comportamento indefinito), quando si ** tenterà di usarlo **, non quando lo si restituisce. –

0

Infatti, è molto raro che si desideri effettivamente restituire un puntatore alla memoria allocata in modo non dinamico. Non credo che causerà un arresto anomalo del programma, una volta che ho scritto una libreria di threading che utilizzava dichiarazioni di array per gonfiare lo stack di runtime e lo spazio di archiviazione per i thread; il fatto che la memoria che sta indicando possa cambiare a caso è ciò che è pericoloso.

0

Uno cannot return static data pointer o di variabile locale dal momento che lo stack disattiva gli oggetti distrutti. Deve essere assegnato dinamicamente ed è sempre il callers responsibility to free poiché la funzione non sa quando liberarlo.

Inoltre, si prega di tenere presente che molte funzioni richiedono speciali deallocaters e fare delete o delete [] non è sempre l'opzione giusta. Potresti finire per corrompere il mucchio! Leggi la documentazione della funzione per tali informazioni.

0

Se è responsabilità del client gestire la memoria restituita, è necessario utilizzare una qualche classe di wrapping che gestisca questa responsabilità. Quando si usa un array, si dovrebbe quasi sempre restituire un vettore std, perché questo gestirà anche gli aspetti dell'array come la dimensione, il ridimensionamento, ecc, ma per un puntatore non elaborato si dovrebbe usare tipicamente std :: auto_ptr o std :: shared_ptr. Se stai scrivendo un modulo destinato a essere usato tra i compilatori, scrivi il tuo. È facile restituire un oggetto proxy che può essere convertito in varie marche di puntatore intelligente.

0

Se le funzioni restituiscono puntatori, quindi utilizzare getData per ottenere l'accesso alla memoria senza assumerne la proprietà e adoptData per l'accesso e la proprietà. Almeno questa è la convenzione che ho visto usato più spesso.

Ma come altri hanno già detto, restituire un contenitore, un puntatore intelligente o qualcos'altro che gestirà automaticamente la memoria. RAII è tuo amico.

E non dimenticare di indicare nei documenti API di entrambe le funzioni se trasferiscono la proprietà o meno. La convention è eccezionale per le persone che lo conoscono, ma deve comunque essere conveniente per coloro che non lo conoscono.

1

In Clang v'è una semplice convenzione (controllarlo fuori here):

  • getX restituisce un puntatore, non la proprietà
  • takeX rendimenti sia puntatore e la proprietà

Tuttavia, come è stato notato, vorrei sollevare le abilità del compilatore qui e restituire un gestore intelligente (puntatore intelligente, contenitore, qualunque) piuttosto che un puntatore nudo quando si restituisce la proprietà.

E 'troppo facile lasciare uno slittamento perdita di memoria in cui tutto ciò che dovete aiutare è una convenzione di denominazione ... si può ancora mantenere la convenzione di denominazione però;)

0

come molti hanno detto, non restituisce un puntatore non elaborato. Se si desidera che i dati vengano eliminati dal richiedente (per qualche motivo), è possibile utilizzare qualcosa come shared_ptr o auto_ptr, nel qual caso il chiamante pulisce il valore restituito (tempo che desidera o no). Se invece si restituisce un membro di una classe che non viene ripulita, allora non si restituisce affatto un puntatore, si restituisce un riferimento (possibilmente const).

Problemi correlati