Se sei nuovo in C ++, potrebbe essere desiderabile evitare di fare grandi lavori in un costruttore. I costruttori sono strettamente legati al modo in cui il linguaggio si comporta e davvero non vuoi che il costruttore venga chiamato inaspettatamente ( explicit
è tuo amico!). I costruttori sono anche piuttosto unici nel senso che non possono restituire un valore. Ciò può rendere più complicata la gestione degli errori.
Questo tipo di raccomandazione diventa meno importante man mano che si impara di più su C ++ e su tutte le diverse cose che possono causare il richiamo dei costruttori. Nel mondo perfetto, la risposta corretta è che la tua biblioteca dovrebbe fare esattamente ciò che l'utente voleva in ogni momento. Molto spesso in C ++ è meglio implementato facendo cose all'interno dei costruttori. Tuttavia, vuoi solo assicurarti di aver capito abbastanza i costruttori da non intrappolare gli utenti in un angolo. Debugare perché il programma funziona male perché un costruttore è stato richiamato quando un utente non ha intenzione di invocarlo può essere un problema.
Allo stesso modo, la gestione delle eccezioni nei costruttori può essere più difficile perché l'ambito in cui si verifica l'eccezione è intrecciato con l'ambito in cui viene creata una variabile, piuttosto che dove si desidera utilizzarlo. Le eccezioni possono essere esasperanti quando si verificano prima che % venga eseguitomain()
, negandoti la possibilità di gestire tale eccezione per l'utente. Questo può accadere se usi variabili globali e gli errori possono essere criptici.
Un'opzione che si trova tra gli estremi è usare i metodi di fabbrica. Se i tuoi utenti si aspettano di utilizzare i metodi di fabbrica, ha senso che potrebbero fare un po 'di inizializzazione in più a quel punto. Un metodo factory non verrà chiamato per caso come potrebbe fare un costruttore, quindi è più difficile rovinarlo.
Una volta che sei sicuro di capire le conseguenze del lavoro svolto in un costruttore, può essere uno strumento potente. L'ultimo esempio di lavoro in un costruttore potrebbe essere la classe lock_guard
. Le guardie di blocco sono costruite passando loro un blocco come un mutex. Chiamano .lock()
su di esso durante il loro costruttore (ovvero facendo un vero lavoro) e .unlock()
su di esso durante il loro distruttore.
Queste classi fanno finta di fare del lavoro reale nel costruttore perché sono progettate per essere utilizzate in questo modo. Chiunque lavori con un lock_guard
è ben consapevole del fatto che il costruttore funziona perché il codice che lo utilizza assomiglia a qualcosa di simile:
{
lock_guard guardMe(myLock); // locks myLock
doSomeStuff();
doMoreStuff();
// as I leave this block, guardMe will unlock myLock. It will do so
// even if I leave via an exception!
}
Se osservi le implementazioni delle classi lock_guard
, troverai facilmente l'80% della loro attenzione o più si concentra su come gestire correttamente i costruttori per evitare sorprese. Implementati correttamente, sono molto potenti. Implementato in modo improprio lock_guards
può essere la rovina della tua esistenza perché può sembrare che facciano una cosa quando ne fa un'altra.