2009-09-26 6 views
19

Immaginate due pezzi simili di codice:Qual è la differenza tra lancio e lancio con arg di eccezione rilevata?

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw err; 
} 

e

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw; 
} 

Sono questi in modo efficace lo stesso o si differenziano in qualche modo sottile? Ad esempio, il primo causa l'esecuzione di un costruttore di copie, mentre forse il secondo riutilizza lo stesso oggetto per ricrearlo?

risposta

26

A seconda di come avete organizzato la vostra gerarchia delle eccezioni, ri-lanciare un'eccezione nominando la variabile eccezione nel istruzione throw può fetta l'oggetto eccezione originale.

Un'espressione laterale senza argomenti si genera l'oggetto eccezione corrente mantenendo inalterato il suo tipo dinamico, che un'espressione laterale con un argomento genererà una nuova fondata sulla statica tipo dell'argomento throw.

E.g.

int main() 
{ 
    try 
    { 
     try 
     { 
      throw Derived(); 
     } 
     catch (Base& b) 
     { 
      std::cout << "Caught a reference to base\n"; 
      b.print(std::cout); 
      throw b; 
     } 
    } 
    catch (Base& b) 
    { 
     std::cout << "Caught a reference to base\n"; 
     b.print(std::cout); 
    } 

    return 0; 
} 

Come scritto in precedenza, il programma sarà in uscita:

Caught a reference to base 
Derived 
Caught a reference to base 
Base

Se il throw b è sostituire con un throw, quindi il fermo esterno sarà anche prendere il Derived eccezione originariamente lanciata. Ciò vale anche se la classe interna recupera l'eccezione Base in base al valore anziché per riferimento, anche se naturalmente ciò significherebbe che l'oggetto eccezione originale non può essere modificato, pertanto le eventuali modifiche a b non si rifletteranno nell'eccezione Derived catturata dal blocco esterno .

+1

Ah, ho completamente dimenticato di tagliare! Dannazione, è importante! Grazie per averlo portato. +1 (Anche se penso che quando hai scritto "... preservando il tipo statico originale ..." intendevi il tipo _dynamic_.Questo è chiamato _dynamic type_, dopotutto, se non il _ "tipo statico originale" _). - – sbi

+1

Great risposta, me ne sono completamente dimenticato. – GManNickG

+0

Sono felice che qualcun altro abbia incontrato il problema _slicing_;) –

16

Nel secondo caso secondo C++ standard 15,1/6 costruttore di copie non viene utilizzato:

laterale espressione senza operando genera nuovamente l'eccezione gestita. L'eccezione si riattiva con il provvisorio esistente; non viene creato alcun nuovo oggetto di eccezione temporaneo. L'eccezione non viene più considerata come bloccata; pertanto, il valore di uncaught_exception() sarà di nuovo vero.

Nel primo caso nuova eccezione saranno gettati secondo 15,1/3:

laterale espressione inizializza un oggetto temporaneo, chiamato l'oggetto eccezione, il tipo di che è determinata eliminando qualsiasi qualificatori di cv di primo livello dal tipo statico dell'operando di lancio e regolazione di il tipo da "matrice di T" o "funzione di ritorno T" a "puntatore a T" o "puntatore a funzione di ritorno T", rispettivamente. < ...> Il temporaneo viene utilizzato per inizializzare la variabile denominata nel gestore corrispondente (15.3). Il tipo dell'espressione di lancio non deve essere un tipo incompleto o un puntatore o un riferimento a un tipo incompleto diverso da void *, const void *, volatile void * o const volatile void *. Fatta eccezione per queste restrizioni e le restrizioni sulla corrispondenza del tipo menzionata in 15.3, l'operando di lancio viene trattato esattamente come un argomento di funzione in una chiamata (5.2.2) o nell'operando di un'istruzione di ritorno.

In entrambi i casi costruttore di copia è richiesto in fase di tiro (15,1/5):

Quando l'oggetto lanciato è un oggetto di classe, e il costruttore di copia utilizzato per inizializzare la copia temporanea, non è accessibile , il programma è mal formato (anche quando l'oggetto temporaneo potrebbe essere altrimenti eliminato). Analogamente, se il distruttore per quell'oggetto non è accessibile, il programma è mal formato (anche se l'oggetto temporaneo potrebbe essere altrimenti eliminato).

+0

Poiché il copyctor deve essere comunque accessibile per l'eccezione originariamente generata, non penso che sia un problema in questo caso. Ma la tua è ancora una buona risposta. +1 – sbi

+0

Sì, ma nel primo caso il c-tor di copia verrà utilizzato due volte. –

+1

Indica quando la dichiarazione specifica un tipo di classe. Tuttavia nel suo caso specifica un tipo di riferimento :) Il riferimento sarà direttamente associato e farà riferimento all'oggetto eccezione. Quindi, avremo bisogno di un copyctor solo al momento del lancio, non catturando in questo caso (farebbe la differenza quando un costruttore di copia base è protetto, per esempio). –