Idealmente:
  • tutti gli errori trasformali in eccezioni (derivate, direttamente o no, da std::exception);
  • cattura solo ad altissimo livello: ad esempio nella main, nei gestori d'eventi dei vari framework, nei thread handler ecc...
  • cattura solo (sempre ad altissimo livello) "const std::exception &" e "..." (oltre alla base dell'eccezioni del framework che stai usando se non è derivata da std::exception.. ad esempio con MFC)

Ovviamente per far ciò DEVI applicare a man bassa tecniche RAII (Resource Acquisition Is Initialization).
Distingui gli errori logici da quelli runtime.

Nel mondo reale, in casi particolari necessiterai di catturare eccezioni a medio basso livello, altre volte ti sarà più comodo gestire l'errore con un if.

Leggiti un po' di informazioni sulle tecniche "Design by Contract".

EDIT: NON lanciare eccezioni nei distruttori (il che significa che le devi catturare tutte anche lì), ma ok il discorso diventa un po' lunghetto
;-)