Domanda

Supponiamo che io sono una classe come questa:

#include <iostream>

using namespace std;

class Boda {
    private:
        char *ptr;

    public:
        Boda() {
            ptr = new char [20];
        }
        ~Boda() {
            cout << "calling ~Boda\n";

            delete [] ptr;
        }

        void ouch() {
            throw 99;
        }
};

void bad() {
    Boda b;
    b.ouch();
}

int main() {
    bad();
}

Sembra che distruttore ~Boda non viene mai chiamato, quindi la risorsa ptr mai ottenere liberato.

Ecco l'output del programma:

terminate called after throwing an instance of 'int'
Aborted

Così sembra la risposta alla mia domanda è No.

Ma ho pensato che la pila ha svolto quando un'eccezione ottenuto gettato? Perché oggetto non Boda b ottenere distrutto nel mio esempio?

Si prega di aiutare a capire questo problema di risorse. Voglio scrivere programmi migliori in futuro.

Inoltre, è questo il cosiddetto RAII?

Grazie, Boda Cydo.

È stato utile?

Soluzione

Se l'eccezione non viene catturato da nessuna parte, quindi il runtime C ++ è libero di andare direttamente a terminare il programma senza fare alcun pila rilassarsi o chiamare qualsiasi distruttori.

Tuttavia, se si aggiunge un blocco try-catch attorno alla chiamata a bad(), si vedrà il distruttore per l'oggetto Boda chiamato:

int main() {
    try {
      bad();
    } catch(...) {  // Catch any exception, forcing stack unwinding always
      return -1;
    }
}

RAII mezzi che dinamicamente (mucchio) memoria allocata è sempre proprietà di un (stack) automaticamente oggetto allocata che rilascia quando il distrugge oggetto. Questo si basa sulla garanzia che il distruttore verrà chiamato quando l'oggetto automaticamente assegnata passa nell'ambito, se a causa di un ritorno normale o causa di un'eccezione.

Questo comportamento angolo-caso non è normalmente un problema per quanto riguarda Raii, dato che di solito il motivo principale che si desidera che i distruttori di corsa è per liberare memoria, e tutta la memoria viene restituita al sistema operativo quando il programma termina in ogni caso. Tuttavia, se i tuoi distruttori fare qualcosa di più complicato, come forse rimuovere un file di blocco su disco o qualcosa del genere, dove sarebbe fare la differenza se il programma chiamato distruttori o meno in caso di caduta, si consiglia di avvolgere il vostro main in un blocco try-catch che le catture tutto (solo per uscita a eccezione comunque) solo per garantire che la pila si snoda sempre prima di terminare.

Altri suggerimenti

Il distruttore non verrà eseguito se si verifica un'eccezione nel costruttore.

Sarà eseguito se necessario (in qualche caso eccezione è gestita) se viene sollevata un'eccezione in un altro metodo come nel tuo esempio. Ma, come il programma viene terminato, chiamando il distruttore non è necessario qui e comportamento dipende del compilatore ...

L'idea di RAII è che alloca le risorse costruttore e distruttore li libera. Se si verifica un'eccezione nel costruttore, non esiste un modo semplice per conoscere le risorse allocate wich dove e che non erano (dipende dalla posizione esatta in cui il costruttore eccezione verificato). Si dovrebbe anche ricordare che, se un costruttore fallisce, l'unico modo per dirlo a chiamante a sollevare un'eccezione e memoria allocata viene liberata (o pila di svolgimento, o heap allocati memoria) come se fosse mai allocato.

La soluzione è ovvia: se ogni eccezione può verificarsi all'interno di un costruttore, si deve prendere e liberare le risorse assegnate, se necessario. Si può effettivamente essere un po 'di codice duplicato con destructor, ma non è un grosso problema.

In distruttore non si deve sollevare eccezioni, in quanto può portare a grandi problemi con pila di svolgimento.

In qualsiasi altro metodo, utilizzare le eccezioni come si vuole, ma non dimenticate di gestirli da qualche parte. Un excception non gestita può essere peggio che non fa eccezione a tutti. So di alcuni programmi che non gestire le eccezioni per alcuni piccoli errori ... e crash per gli errori che dovrebbe emettere solo un avvertimento.

Prova il lavaggio del flusso - si vedrà che il distruttore è infatti chiamata:

cout << "calling ~Boda" << endl;

E 'il buffer di I / O che i ritardi nella stampa, al punto che i tagli di terminazione programma in prima uscita effettiva.

Modifica:

È possibile che questo vale per eccezioni gestite . Con le eccezioni non gestite lo standard non specifica se stack è svolto oppure no. Vedi anche questa domanda SO .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top