2013-07-17 13 views
29

Per esempio:Un distruttore viene chiamato quando un oggetto esce dall'oscilloscopio?

int main() { 
    Foo *leedle = new Foo(); 

    return 0; 
} 

class Foo { 
private: 
    somePointer* bar; 

public: 
    Foo(); 
    ~Foo(); 
}; 

Foo::~Foo() { 
    delete bar; 
} 

Sarebbe il distruttore essere implicitamente chiamato dal compilatore o ci sarebbe una perdita di memoria?

Sono nuovo nella memoria dinamica, quindi se questo non è un caso di test utilizzabile, mi dispiace.

+4

No, è necessario chiamare 'delete leedle'. – juanchopanza

+0

Puoi fare una risposta e approfondire perché? – Tux

+3

Sì alla domanda nel titolo, No alla domanda nel corpo. Devi cancellare manualmente tutto ciò che è nuovo. Se si usa 'new' nel costruttore (a meno che non siano generate eccezioni) è possibile chiamare' delete' nel distruttore e pulirà la memoria per voi. – Rapptz

risposta

71

Sì, le variabili automatiche verranno distrutte alla fine del blocco di codice che la racchiude. Ma continua a leggere.

Il titolo della domanda chiede se verrà chiamato un distruttore quando la variabile esce dall'ambito. Presumibilmente, ciò che intendevi chiedere era:

il distruttore di Foo sarà chiamato alla fine di main()?

Dato il codice che hai fornito, la risposta a questa domanda è no poiché l'oggetto Foo ha una durata di storage dinamica, come vedremo tra breve.

Nota qui ciò che la variabile automatica è:

Foo* leedle = new Foo(); 

Qui, leedle è la variabile automatico che sarà distrutto. leedle è solo un puntatore. La cosa che leedle punta a non corrisponde a ha una durata di archiviazione automatica e non verrà distrutta. Quindi, se si esegue questa operazione:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
} 

Hai perdite la memoria allocata da new leedle.


È mustdelete tutto ciò che è stato assegnato con new:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
    delete leedle; 
} 

Ciò è reso molto più semplice e più robusto, utilizzando puntatori intelligenti. In C++ 03:

void DoIt() 
{ 
    std::auto_ptr <Foo> leedle (new Foo); 
} 

o C++ 11:

void DoIt() 
{ 
    std::unique_ptr <Foo> leedle = std::make_unique <Foo>(); 
} 

puntatori intelligenti sono utilizzati come variabili automatiche, come sopra, e quando escono di portata e sono distrutte, automaticamente (nel distruttore) delete l'oggetto puntato. Quindi, in entrambi i casi sopra, non c'è perdita di memoria.


Proviamo a chiarire un po 'di linguaggio qui. In C++, le variabili hanno una durata di archiviazione. In C++ 03 ci sono 3 durate di memorizzazione:

1: automatico: Una variabile con durata di archiviazione automatica verrà distrutta alla fine del blocco di codice che la racchiude.

Considerate:

void Foo() 
{ 
    bool b = true; 
    { 
    int n = 42; 
    } // LINE 1 
    double d = 3.14; 
} // LINE 2 

In questo esempio, tutte le variabili hanno durata memorizzazione automatica. Sia b e d verranno distrutti a LINE 2. n verranno distrutti a LINE 1.

2: statico: Una variabile con durata di conservazione statica sarà assegnata prima che il programma inizia, e distrutta quando il programma termina.

3: dinamica: Una variabile con durata di conservazione dinamica verrà allocata quando si assegnano utilizzando funzioni di allocazione di memoria dinamica (ad esempio, new) e verranno distrutti quando distruggere utilizzando funzioni di allocazione di memoria dinamica (ad esempio, delete).

Nel mio esempio originale sopra:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
} 

leedle è una variabile con durata di conservazione automatica e verranno distrutti alla brace fine. La cosa a cui punta leedle ha durata di archiviazione dinamica e non viene distrutta nel codice sopra. È necessario chiamare delete per deallocarlo.

C++ 11 aggiunge anche una quarta durata di conservazione:

4: filetto: variabili con durata di conservazione filo sono assegnati quando inizia il filo e deallocate quando le estremità del filo.

+0

Grazie, questo risponde alla mia domanda! :) – Tux

+2

Grande risposta - Trovo questo argomento affrontato in modo molto efficace, sottolineando innanzitutto che il puntatore [automatico] _will_ viene distrutto, prima di passare alla discussione della semantica dell'oggetto dinamico [dinamico]. Con la correzione di Eric in atto, questa risposta è perfetta. –

+0

@LightnessRacesinOrbit: dai un'occhiata, ho fatto un'altra modifica. –

2

ci sarebbe una perdita di memoria davvero. Il distruttore dell'oggetto che esce dall'ambito (il Foo *) viene chiamato, ma quello per l'oggetto puntato (il Foo che hai assegnato) non lo fa.

Tecnicamente parlando, dal momento che ci si trova nel principale, non si tratta di una perdita di memoria, poiché fino a quando l'applicazione non viene terminata è possibile accedere a ogni variabile allocata. Con questo proposito, cito Alexandrescu (da Modern C++, il capitolo relativo single)

perdite di memoria vengono visualizzate quando si assegnano accumulare dati e perdere tutti i riferimenti ad esso. Questo non è il caso qui: Nulla si sta accumulando, e siamo a conoscenza della memoria allocata fino alla fine dell'applicazione . Inoltre, tutti i moderni

Naturalmente, questo non implica che non si deve chiamare delete, in quanto sarebbe una pratica estremamente cattivo (e pericoloso).

+1

Potresti elaborare un po 'di più. – Tux

+0

scusate, ho inviato per sbaglio mentre lo scrivevo –

-5

In questo caso, quando il ritorno principale è la fine del programma, il sistema operativo gestirà la liberazione di tutte le risorse. Se, ad esempio, si trattasse di un'altra funzione, è necessario utilizzare l'opzione Elimina .

+1

Vero, ma non potete semplicemente fare affidamento sul sistema operativo per fare tutto il lavoro per voi. ;) – Tux

+5

-1 per insegnare le cattive pratiche a un principiante ovvio. Inoltre, lo standard C++ non garantisce che il sistema operativo recuperi le risorse o che i sistemi operativi esistano. –

+0

E anche in questo caso il sistema operativo probabilmente reclamerebbe semplicemente la memoria chiamando il distruttore. –

5

Sì, se un oggetto esce dall'ambito, viene chiamato il distruttore. MA No, il distruttore non verrà chiamato in questo caso, perché hai solo un puntatoredi portata, che non ha puntatore particolare distruttore, quindi non ci sarà alcuna chiamata indiretta al distruttore Foo s'.

Questo esempio è il dominio dell'applicazione di puntatori intelligenti come std::unique_ptr e std::shared_ptr. Quelle sono classi reali che, a differenza dei puntatori grezzi hanno un distruttore, (condizionatamente) che chiama delete sull'oggetto puntato.

Btw, distruttore Foo s' cancella bar, fresa bar non è mai stato inizializzato, né assegnato a un indirizzo che punta a un oggetto reale, quindi la chiamata di eliminazione darà un comportamento indefinito, probabilmente un incidente.

1

Innanzitutto, il codice non viene compilato; new restituisce un puntatore a un oggetto allocato nell'heap. Hai bisogno di:

int main() { 
    Foo *leedle = new Foo(); 
    return 0; 
} 

Ora, dal momento che new assegna l'oggetto con archiviazione dinamica, invece di automatico, non sta andando fuori portata al termine della funzione. Quindi non verrà cancellato e hai perso memoria.

+0

Si prega di smettere con il "heap" obsoleto vs "stack" gubbins. –

+1

Cosa c'è che non va, @LightnessRacesinOrbit? – Joni

+3

È una perdita di astrazione. Il linguaggio consente di creare oggetti con durata di archiviazione automatica, statica e dinamica. Dove finiscono nella memoria fisica o virtuale non è né importante né specificato dalla lingua. Ripetendo "nello stack" e "nell'heap" aiuta solo a propagare questo incubo ... –

Problemi correlati