2013-02-05 12 views
9

Ho una classe che può generare un'eccezione nel suo costruttore. Come posso dichiarare un'istanza di quella classe in un blocco try/catch, pur continuando a renderla disponibile nel giusto ambito?Approccio RAII alla cattura delle eccezioni del costruttore

try { MyClass lMyObject; } 
catch (const std::exception& e) { /* Handle constructor exception */ } 

lMyObject.DoSomething(); // lMyObject not in scope! 

Esiste un modo alternativo per raggiungere questo obiettivo, nel rispetto del RAII linguaggio?

Preferirei non utilizzare un metodo init() per la costruzione in due fasi. L'unica altra cosa che ho potuto venire con era:

MyClass* lMyObject; 

try { lMyObject = new MyClass(); } 
catch (const std::exception& e) { /* Handle constructor exception */ } 

std::shared_ptr<MyClass> lMyObjectPtr(lMyObject); 
lMyObjectPtr->DoSomething(); 

funziona bene, ma non sono felice con il puntatore prima in ambito e puntatore indiretto. È solo un'altra verruca C++?

+2

Spostare la chiamata del metodo DoSomething() al blocco try? – milleniumbug

+1

** Mai ** cattura le eccezioni per copia. Attacca con * riferimenti costanti *. –

+1

Nel secondo esempio, se il costruttore ha gettato, 'lMyObject' è un puntatore non inizializzato. – aschepler

risposta

5

Se un costruttore genera ciò significa che l'oggetto non è stato inizializzato e quindi non è riuscito ad avviarne l'esistenza.

MyClass* lMyObject; 
try { lMyObject = new MyClass(); } 
catch (std::exception e) { /* Handle constructor exception */ } 

Nella suddetta se il costruttore genera un'eccezione, lMyObject rimane inizializzato, in altre parole, il puntatore contiene un valore indeterminato.

Vedi classico Constructor Failures per una spiegazione dettagliata:

Potremmo riassumere il modello costruttore di C++ come segue:

O:

(a) Il costruttore restituisce normalmente raggiungendo la sua fine o una dichiarazione di ritorno, e l'oggetto esiste.

Oppure:

(b) Le uscite costruttore attraverso l'emissione di un'eccezione, e l'oggetto non solo non ora esiste, ma non è mai esistito.

Non ci sono altre possibilità.

0

che non è necessario usare shared_ptr, utilizzare unique_ptr:

std::unique_ptr<MyClass> pMyObject; 
try { pMyObject.reset(new MyClass()); } 
catch (std::exception &e) { /* Handle constructor exception */ throw; } 
MyClass &lMyObject = *pMyObject; 

lMyObject.DoSomething(); 

Ovviamente, è vostra responsabilità di garantire che il programma non rientra attraverso il blocco catch senza né inizializzazione pMyObject, o in uscita dal funzione (ad esempio tramite return o throw).

Se disponibile, è possibile utilizzare Boost.Optional per evitare l'uso di memoria heap:

boost::optional<MyClass> oMyObject; 
try { oMyObject.reset(MyClass()); } 
catch (std::exception &e) { /* Handle constructor exception */ throw; } 
MyClass &lMyObject = *oMyObject; 

lMyObject.DoSomething(); 
+0

Dereferisce il puntatore 'NULL' se viene generata un'eccezione. –

+0

@MaximYegorushkin che dipende interamente da cosa '/ * Gestisce l'eccezione del costruttore * /'. – ecatmur

+0

Un buon test per il tuo consiglio è quello di sostituire "/ * Gestire l'eccezione del costruttore * /' con una stringa vuota. Il tuo consiglio fallisce questo test. –

-1

È possibile impostare MyClass del costruttore di copia per accettare l'input dei rifiuti, così dichiarando effettivamente un puntatore con la vostra dichiarazione di dell'oggetto. Poi si potrebbe manually call il costruttore di default all'interno del blocco try:

MyClass lMyObject(null); // calls copy constructor 
try { 
    new (lMyObject) MyClass(); // calls normal constructor 
} 
catch (const std::exception& e) { /* Handle constructor exception */ } 

lMyObject.DoSomething(); 
+0

Chiama costruttore di 'MyClass' due volte, distruttore una volta . –

+0

A destra: affinché questo modello funzioni, è necessario scrivere il costruttore della copia in modo tale che non abbia assegnato risorse. È un po 'hackerato. –

1

Costruttori sono per mettere un oggetto in uno stato coerente e stabilire invarianti di classe.Consentire a un'eccezione di sfuggire a un costruttore significa che qualcosa nel costruttore non è stato completato, quindi ora l'oggetto si trova in uno stato strano sconosciuto (che potrebbe includere anche perdite di risorse). Dal momento che il costruttore dell'oggetto non è stato completato, il compilatore non provocherà nemmeno il suo distruttore. Forse quello che stai cercando è catturare l'eccezione all'interno del costruttore. Supponendo che non sia riconsiderato, ciò dovrebbe indurre il costruttore a completare l'esecuzione e l'oggetto è ora completamente formato.

2

Il miglior modo di scrivere il codice è questo: -

MyClass lMyObject; 
lMyObject.DoSomething(); 

Nessun Trys, catture, o puntatori.

Se il costruttore lancia, DoSomething non può essere richiamato. Il che è giusto: se il costruttore ha gettato, l'oggetto non è mai stato costruito.

E, soprattutto, non catturare (o persino prendere/rilanciare) le eccezioni a meno che tu non abbia qualcosa di costruttivo da fare con loro. Lascia che le eccezioni facciano il loro lavoro e aumentino fino a quando qualcosa che sa come gestirle può fare il suo lavoro.

+0

Grazie per il suggerimento, penso che sia un buon punto. –

Problemi correlati