Ha la memoria ottenere rilasciato quando ho un'eccezione?
-
19-09-2019 - |
Domanda
Stavo discutendo con alcuni colleghi su ciò che accade quando si lancia un'eccezione in una classe allocata dinamicamente. So che malloc
viene chiamato, e poi il costruttore della classe. Il costruttore non ritorna mai, quindi cosa succede al malloc
?
Si consideri il seguente esempio:
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 succede al o
di memoria malloced, ci si perde? Ha CRT cattura l'eccezione del costruttore e deallocare la memoria?
Cheers!
Ricco
Soluzione
Una chiamata a
new B();
risolve in due cose:
- imputare con un operatore new () (sia globale uno o uno specifico classe, potenzialmente un posizionamento uno con l'
new (xxx) B()
sintassi) - chiamando il costruttore.
Se il tiro del costruttore, l'operatore corrispondente eliminazione viene chiamato. Il caso in cui il corrispondente di eliminazione è un collocamento è l'unico caso in cui un collocamento eliminare operatore è chiamato senza la sintassi :: operator delete (). delete x;
o delete[] x;
non chiamare il posizionamento cancellare gli operatori e non c'è una sintassi simile alla nuova collocazione a chiamarli.
Si noti che mentre il distruttore di B sarà non essere chiamato, già costruiti sottooggetti (membri o B e di base classi di B) sarà distrutto prima della chiamata all'operatore cancellare. Il costruttore che non si chiama è quello per B.
Altri suggerimenti
Quando viene generata un'eccezione dal costruttore, la memoria allocata da nuovo viene rilasciato, ma il distruttore della classe B non viene chiamato.
In questo caso, l'oggetto, o, in realtà non ottenere costruito, e la memoria allocata da new viene liberato. 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 butta nel costruttore - tanto come il vostro esempio. Quindi se avete un oggetto valido, si ha la risorsa.
Se l'oggetto è costruito, allora si hanno acquisito con successo la risorsa. Ciò significa che per la vita dell'oggetto, è proprio la risorsa. Quando l'oggetto viene eliminato, la risorsa viene rilasciato. Se l'oggetto non viene mai costruito, allora non avete mai acquistato la risorsa. Vedi Wikipedia:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
Dalla C ++ 2003 Standard 5.3.4 / 17 - Nuovo:
Se una parte dell'inizializzazione dell'oggetto sopra descritto termina lanciando una funzione deallocazione adatto eccezione e può essere trovato, la funzione deallocazione è chiamato a liberare la memoria in cui era in costruzione l'oggetto, dopo che l'eccezione continua a propagarsi nel contesto della nuova espressione. Se nessuna funzione di corrispondenza deallocazione univoca può essere trovato, propagare l'eccezione non causa la memoria dell'oggetto per essere liberato. [Nota: Questo è appropriato quando la detta funzione di allocazione non alloca memoria; in caso contrario, è suscettibile di provocare 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 deallocatore adatto, il compilatore è responsabile per il cablaggio in una chiamata ad esso se il costruttore getta.
Si noti che questo è più o meno estranei a ciò che accade a risorse acquisite nel costruttore, che è ciò che il mio primo tentativo di risposta discusso -. Ed è una questione che viene discussa in molte domande frequenti, articoli e messaggi