2009-08-20 19 views
5

Stavo discutendo con alcune università su cosa succede quando si lancia un'eccezione in una classe allocata dinamicamente. So che viene chiamato malloc, e quindi il costruttore della classe. Il costruttore non ritorna mai, quindi cosa succede al malloc?La memoria viene rilasciata quando lancio un'eccezione?

Si consideri il seguente

class B 
{ 
    public: 
     B() 
     { 
      cout << "B::B()" << endl; 
      throw "B::exception"; 
     } 

     ~B() 
     { 
      cout << "B::~B()" << endl;   
     } 
}; 

void main() 
{ 
    B *o = 0; 
    try 
    { 
     o = new B; 
    } 

    catch(const char *) 
    { 
    cout << "ouch!" << endl; 
    } 
} 

Cosa accade alla memoria malloced 'O', ci si perde? Il CRT cattura l'eccezione del costruttore e rilascia la memoria?

Acclamazioni Rich

+0

Risposta breve: Sì. Vedere la risposta di Cătălin Pitiş per i dettagli. –

risposta

9

Una chiamata

new B(); 

risolve in due cose:

  • ripartizione con un operatore new() (sia globale uno o uno specifico classe, potenzialmente un posizionamento unico con la sintassi new (xxx) B())
  • chiamando il costruttore.

Se il costruttore lancia, viene richiamato l'operatore corrispondente. Il caso in cui l'eliminazione corrispondente è un posizionamento uno è l'unico caso in cui viene chiamato un operatore di eliminazione del posizionamento senza la sintassi :: operator delete(). delete x; o delete[] x; non chiamare gli operatori di eliminazione del posizionamento e non esiste una sintassi simile al posizionamento nuovo per chiamarli.

Si noti che mentre si chiama il distruttore di B non, i sottoprogetti già costruiti (membri o B e classi base di B) verranno distrutti prima della cancellazione dall'operatore all'operatore. Il costruttore che non è chiamato è quello per B.

+0

Importante addendum: il costruttore per B non è stato eseguito, non c'è mai stato un oggetto di quel tipo. Se è stato chiamato un nuovo() nel costruttore di B prima che l'eccezione fosse attivata, è * non * stato rilasciato - e non si ha più un puntatore a quella memoria, quindi non è possibile cancellarlo(). * KEEP AWAY * from new() nei costruttori. – DevSolar

+0

Se si utilizzano tecniche RAII (ad esempio, con std :: auto_ptr), new() nei costruttori non è un grosso problema. –

+0

@DevSolar: tutto dipende da dove viene memorizzato il risultato del nuovo. Se si trova in un puntatore intelligente, etere locale al costruttore o un membro della classe B o dei suoi genitori, quell'oggetto verrà distrutto e la memoria allocata all'interno del costruttore di B ma prima del lancio dell'eccezione verrà liberata. Ovviamente se fai un nuovo Foo(); getta "bar", il ricordo non verrà liberato e il Foo non sarà distrutto. In un costruttore o altrove. Chiarirò che il distruttore di B non viene chiamato. – AProgrammer

6

Quando viene generata un'eccezione dal costruttore, la memoria allocata dalla nuova viene rilasciato, ma il distruttore di classe B non è chiamato.

+2

Sì. Solo per completare la tua risposta ... C'è una buona ragione per cui il distruttore non viene chiamato: l'oggetto non è mai stato creato. I distruttori sono chiamati solo per oggetti esistenti in un determinato periodo di tempo. E quelli sono i casi in cui il costruttore è stato completato con successo. –

+1

Sì, hai ragione. Un oggetto è considerato creato (quindi distruttibile) solo dopo l'esecuzione corretta del costruttore. –

+0

Nota: vengono chiamati il ​​destrucotr di tutti i membri e le classi di base completamente costruite. –

2

In questo caso, l'oggetto, o, non viene effettivamente creato e la memoria allocata da nuovo viene liberata. Come tale, il distruttore non viene chiamato. Quindi non c'è bisogno di chiamare:

delete o; 

Un design pattern interessante è RAII - Risorsa acquisizione è di inizializzazione. In questo modello, si utilizza un costruttore per incapsulare l'acquisizione di una risorsa e rilasciare la risorsa nel distruttore. Se la risorsa non può essere acquisita, si introduce il costruttore - proprio come il tuo esempio. Quindi se hai un oggetto valido, hai la risorsa.

Se l'oggetto è stato creato, la risorsa è stata acquisita correttamente. Ciò significa che per la vita dell'oggetto, tu possiedi la risorsa. Quando l'oggetto viene cancellato, la risorsa viene rilasciata. Se l'oggetto non viene mai costruito, non hai mai acquisito la risorsa. Vedi Wikipedia:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

+0

Di conseguenza, se il costruttore ha eseguito il lavoro di acquisizione delle risorse _some_ prima di attivare l'eccezione, il lavoro del costruttore deve essere ripulito prima del lancio. – Stewart

1

Dalla C++ 2003 Standard 5.3.4/17 - Nuovo:

Se una parte dell'inizializzazione scopo sopra descritto termina lanciando una funzione deallocazione adatto eccezione e può essere trovato, la funzione deallocazione è chiamato a liberare la memoria in cui è stato essendo l'oggetto costruito, dopo il quale l'eccezione continua a propagarsi nel contesto della nuova espressione. Se non è possibile trovare alcuna funzione di deallocazione di corrispondenza non ambigua, la propagazione dell'eccezione non causa la liberazione della memoria dell'oggetto. [Nota: questo è appropriato quando la funzione di allocazione chiamata non alloca memoria; in caso contrario, è probabile che si verifichi una perdita di memoria. ]

Quindi non può o non può essere una perdita - dipende dal fatto che un deallocatore appropriata può essere trovata (che normalmente è il caso, a meno che operator new/delete sono stato uno scostamento) .Nel caso in cui ci sia un adatto deallocator, il compilatore è responsabile per il cablaggio in una chiamata ad esso se il costruttore lancia.

Si noti che questo è più o meno estraneo a ciò che accade alle risorse acquisite nel costruttore, che è il mio primo tentativo di risposta discusso - ed è una domanda che viene discussa in molte domande frequenti, articoli e post.

+0

Questo era veramente destinato a rispondere alla domanda (duplicata?) Qui: http://stackoverflow.com/questions/1674980/who-deletes-the-memory-allocated-during-a-new-operation-which-has-exception -in –

Problemi correlati