2010-07-10 13 views
8

Qual è una buona politica per quando utilizzare "nuovo" per creare un'istanza di una classe? Sono stato di programmazione C++ passatempo per un po ', ma io non sono ancora con certezza quando è il momento migliore per farlo:quando usare nuovo in C++?

MyClass thing(param1, param2); 

su questo:

MyClass* thing; 
thing = new MyClass(param1, param2); 

Qualche consiglio?

+0

Sì. Il mio consiglio è di leggere questo libro: http://www2.research.att.com/~bs/3rd.html in particolare capitolo 5: Puntatori, matrici e strutture –

+2

Capisci in primo luogo perché potrebbe essere necessario creare oggetti dinamicamente? –

risposta

0
MyClass thing(param1, param2); //memory for thing is allocated on the process stack(static allocation) 

MyClass* thing; 
thing = new MyClass(param1, param2); //memory is allocated dynamically on the heap(free store) for thing 

La differenza sta qui:

int main() 
{ 
    { 
    MyClass thing(param1, param2); //thing is local to the scope 
    } //destructor called for thing 
    //cannot access thing (thing doesn't exist) 
} 

int main() 
{ 
    { 
    MyClass* thing; 
    thing = new MyClass(param1, param2); 
    } 
    //the object pointed to by thing still exists 
    //Memory leak 
} 

oggetti di grandi è necessario allocare memoria dinamicamente (utilizzare nuovo) perché lo stack processo ha una dimensione limitata.

+0

Bene, non è ancora possibile accedere a 'thing' dopo la fine dell'ambito. :) Esiste ancora però, e dal momento che non ci sono più puntatori che puntano ad esso, non può mai essere rilasciato. – GManNickG

+0

@GMan: Sì, hai ragione. Il mio post richiede alcune correzioni minori. –

+1

'thing' il MyClass * non esisterà più dove rivendichi, perché era una variabile locale che andava fuori dal campo di applicazione. L'oggetto MyClass - quel 'cosa 'usato per puntare a - persisterà, e questo oggetto è ciò che causa la perdita di memoria. –

2

La regola generale è: se funziona senza new, non utilizzare new.

+3

Questa è una cattiva idea. Potrebbe sembrare che funzioni mentre passi il puntatore, fino a quando il frame dello stack non viene liberato. Poi all'improvviso hai un riferimento al nulla. Il debug è abbastanza difficile senza aggiungere codice di prova ed errore al mix. – Borealid

+0

Ma non c'è alcun puntatore coinvolto nella cosa 'MyClass (param1, param2);'. Se puoi usare oggetti come tu usi un 'int', dovresti. –

+3

se qualcuno che non conosce la differenza tra lo stack e l'heap (come evidente dalla domanda) inizia 'coding by trial and error', è tenuto a scoprire che può restituire '& myStackobj', e 'sembra funzionare '. – Javier

5

Il primo approccio crea un'istanza locale nello stack che va via quando si chiude la funzione di chiamata. Il secondo crea un'istanza che rimane nell'heap fino a quando (e se) la rilasci esplicitamente. La scelta dipende dal tipo di controllo e durata che desideri per il tuo oggetto.

2

In generale: non è necessario utilizzare new se si intende eliminare l'oggetto nello stesso ambito. Se l'oggetto è abbastanza grande, potresti voler usare new.
Si consiglia di esaminare la differenza tra memoria heap e stack se si desidera conoscere i dettagli.

22

Per quanto riguarda la progettazione, utilizzare l'allocazione automatica (stack) il più possibile. Ogni volta che è necessario estendere la durata di un oggetto oltre un determinato ambito, allocarlo dinamicamente.

E anche così, mai allocare dinamicamente le cose prime. Mantenerli sempre avvolti in una sorta di wrapper che implementa Scope-Bound Resource Management (SBRM, noto inizialmente con il nome stupido/imbarazzante Acquisizione risorse è inizializzazione o RAII.) Cioè, le allocazioni dinamiche dovrebbero essere mantenute in oggetti automatici che puliranno su automaticamente!

Un buon esempio di questo è std::vector: non si può perdere la memoria interna a vector, perché è distruttore viene eseguito in ogni scenario quando la memoria deve essere liberata, e lo libererà per voi. auto_ptr è il primo e unico puntatore intelligente disponibile nella libreria standard, ma è piuttosto brutto. È meglio usare shared_ptr o molti altri popolari puntatori intelligenti disponibili in Boost e/o TR1 e/o C++ 0x.

performance, oggetti allocati in pila può essere fatto in modo molto rapidamente (la dimensione dello stack viene aumentata per-funzione-chiamata, in modo tutta la memoria richiesta è stato allocato up-front da un semplice movimento di una pointer.) Contrariamente, l'allocazione dinamica richiede generalmente molto più tempo. È abbastanza possibile ottenere allocazioni dinamiche veloci con schemi di allocazione personalizzati, ma anche i migliori saranno ancora più lenti dell'allocazione dello stack.

Occasionalmente, potresti scoprire che passi troppo tempo a copiare gli oggetti. In questo caso, potrebbe valerne la pena allocarlo dinamicamente e spostare semplicemente i puntatori. Tuttavia, si prega di notare che ho detto "trova". Questo tipo di cambiamento è qualcosa che trovi profilando e misurando, mai indovinando.

Quindi: allocazione automatica quando possibile, allocazione dinamica quando necessario.

+2

+1 per la risposta di presley :-) –

+2

+1 per "solo quando necessario" – rubenvb

+2

+1 in particolare per chiamare RAII un nome stupido e scomodo, che è. – Dan

1

Per prima cosa, poniti la domanda, ha senso che l'oggetto venga copiato quando un'altra funzione lo desidera?

Se ha senso copiare l'oggetto, la soluzione migliore è creare tutto nello stack o come variabili membro e quindi passare semplicemente le copie quando necessario.

Se non ha senso copiare l'oggetto, è necessario utilizzare il modulo new in modo da poter passare in modo sicuro il puntatore all'oggetto. Devi usare un puntatore (o riferimento) perché, come notato, non ha senso copiare l'oggetto.

Ci sono due eccezioni io sappia:

Se si conosce l'oggetto non sta per essere utilizzato dopo la funzione corrente è terminata, è possibile creare l'oggetto in pila in modo che sia eliminata . Fai in modo che nessuno tenga il puntatore in seguito! (Raramente lo trovo, ma succede)

Se l'oggetto è utilizzato internamente da un'altra classe che non deve essere copiata, è sufficiente inserirla come variabile membro. Dal momento che l'oggetto in cui è inserito non verrà copiato, ed è solo per uso interno che sarà sicuro.