2015-02-05 17 views
12

Quando si esce dall'ambito del blocco catch, viene richiamato il distruttore di eccezioni? (Nel caso in cui non lo si rilancia)eccezione con distruttore non virtuale C++

Supponiamo che abbia classe A e che il suo distruttore non sia virtuale. B eredita A. Supponiamo qualche funzione gettato oggetto della classe B come eccezione, ed è stato catturato da un blocco catch

catch(A& a){ 
... 
} 

Se il distruttore eccezione dovrebbe essere chiamato quando andare fuori portata di cattura, in in questo caso verrà chiamato solo il distruttore della classe base A?

Cornstalks: live trial risultato nel chiamare entrambi distruttore di classe.

Contrasta la mia logica. Spiegare qualcuno?

+1

Sono un po 'interessato al motivo per cui fai questa domanda; è una domanda molto valida, non molto di base, ma indica che ti interessa il momento in cui viene chiamato il distruttore della tua eccezione, che normalmente non farebbe. –

+0

Huh, non sono sicuro di quest'ultima parte. –

+0

@ MarcusMüller: Cosa c'è di sbagliato nel voler acquisire conoscenze sugli strumenti che usiamo? –

risposta

5

OK, qualcuno ha già risposto alla tua prima domanda. Mi concentrerò su questo:

se il distruttore di eccezioni deve essere chiamato quando si esce dal campo di cattura, in questo caso verrà chiamato solo il nome della classe base A?

L'implementazione distruggerà sempre correttamente l'oggetto eccezione indipendentemente da come viene rilevato. L'implementazione costruisce l'oggetto eccezione, quindi sa come distruggerlo. Questo non è lo stesso di quando si chiama delete attraverso un puntatore, perché in quel caso ci sono informazioni incomplete sul tipo completo dell'oggetto in quel punto (potrebbe essere stato new ed altrove) a meno che non esista un distruttore virtuale.

Se questo non fosse il caso, catch (...) non funzionerebbe mai.

+0

(potrebbe essere stato aggiornato da qualche altra parte) - puoi spiegare, per favore? –

+3

E, solo per completezza, eliminare un puntatore a un oggetto derivato attraverso un puntatore al tipo base, quando il tipo base non ha un distruttore virtuale, produce ** comportamento non definito **. Potrebbe eseguire il distruttore di base, ma potrebbe fare qualcosa di completamente diverso. –

+0

@Day_Dreamer Mi dispiace, è stata una frase un po 'imbarazzante, ma il punto è che nel punto in cui il puntatore è 'delete'd, non c'è modo di farlo corrispondere al punto in cui lo stesso oggetto era' new'ed. Ecco perché è necessario il distruttore virtuale, altrimenti non è possibile che il codice "sappia" quale distruttore invocare. – Brian

5

quando si esce dall'ambito del blocco catch, viene chiamato il distruttore di eccezioni? (Nel caso in cui non ci si rigenerare)

Sì:

[C++11: 15.1/4]:[..] L'oggetto eccezione viene distrutto dopo sia l'ultima rimanente gestore attivo per le uscite di eccezione con qualsiasi mezzo diverso dal rethrowing o l'ultimo oggetto di tipo std::exception_ptr (18.8.5) che fa riferimento all'oggetto eccezione viene distrutto, a seconda di quale dei due è successivo. [..]


se il distruttore eccezione dovrebbe essere chiamato quando andare fuori del campo di applicazione cattura, in questo caso solo la classe di base di una d'tor si chiamerà?

No:

#include <iostream> 

struct A 
{ 
    A() { std::cout << "A()"; } 
    A(const A&) { std::cout << "A(const A&)"; } 
    A(A&&) { std::cout << "A(A&&)"; } 
    ~A() { std::cout << "~A()"; } 
}; 

struct B : A 
{ 
    B() { std::cout << "B()"; } 
    B(const B&) { std::cout << "B(const B&)"; } 
    B(B&&) { std::cout << "B(B&&)"; } 
    ~B() { std::cout << "~B()"; } 
}; 

int main() 
{ 
    try { 
     throw B(); 
    } 
    catch (A&) { 
    } 
} 

// Output: A()B()~B()~A() 
+0

In realtà, il tuo campione indica il contrario. Ottengo il seguente output (usando lo stesso compilatore online): ~ B() ~ A() – zdan

+1

Ahem, Lightness, il tuo collegamento coliru mostra sia '~ B()' che '~ A() stampato ... che è cosa conferma la risposta di Cornstalks ... Basta dire ' –

+0

@InnocentBystander: immagino sia impossibile dire se '~ B()' è il temporaneo nella _throw-expression_, o se viene eliso (che è legale) e noi stiamo vedendo l'output dal 'catch'. –

3

Mentre io non sto citando dallo standard, sembra che gettare un B e la cattura di un A& si tradurrà in entrambi i A 's e B' s distruttori sempre chiamato. Live demo:

#include <iostream> 

struct A 
{ 
    ~A() { std::cout << "A::~A" << std::endl; } 
}; 

struct B : public A 
{ 
    ~B() { std::cout << "B::~B" << std::endl; } 
}; 

void throwit() 
{ 
    throw B{}; 
} 

int main() 
{ 
    std::cout << "beginning main scope" << std::endl; 

    { 
     std::cout << "beginning inner scope" << std::endl; 

     try 
     { 
      std::cout << "calling throwit()" << std::endl; 
      throwit(); 
     } 
     catch (A& a) 
     { 
      std::cout << "caught exception" << std::endl; 
     } 

     std::cout << "ending inner scope" << std::endl; 
    } 

    std::cout << "ending main scope" << std::endl; 
} 

uscita:

cominciando scopo principale
cominciando portata interna
chiamando throwit()
eccezione catturato
B :: ~ B
A :: ~ A
end scope interno
terminazione portata principale

Come potete vedere, entrambi i distruttori vengono chiamati. La stampa extra scope mostra in modo molto chiaro esattamente quando vengono chiamati i distruttori (alla fine del blocco catch).

3

Ogni volta che lo standard dice che un oggetto viene distrutto, significa che viene richiamato il distruttore più derivato corretto.

Sempre.

Quando si elimina polimorficamente un oggetto senza un distruttore virtuale o si termina (tramite l'operatore delete o una chiamata distruttiva esplicita) un oggetto di tipo incompleto e il distruttore appropriato non è banale, lo standard non dice che l'oggetto è distrutto . Non dice che il distruttore della classe base è invocato. Dice che hai un comportamento indefinito.