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
;-)