Newsgroup: it.comp.lang.c++
Titolo: Throw e vincoli di restrizione

Salve,
avrei una domanda da porvi: quando una classe derivata ridefinisce un metodo
della sua classe base, se tale metodo
solleva delle eccezioni, il metodo ridefinito deve essere restrittivo tanto
quanto (se non di più) il metodo che ridefinisce.
Ma questa regola precisamente oltre ad obbligare il programmatore a non
definire metodi che potrebbero sollevare eccezioni che il gestore di
eccezioni per il metodo della classe base non sa gestire, che vantaggi
offre?
Cioè dato il seguente codice (errato):
#include <iostream>
struct A {
 virtual void f() throw(int) {
  throw 4;
 };
};
struct B : public A {
  void f() {}; // Puo' sollevare qualsiasi eccezione
};
int main() {
    return 0;
}
Se B::f() solleva un'eccezione che non e' contemplata dalla lista di tipi di
eccezioni di
A::f() che problemi ci sono? Evidentemente sara' opportuno gestire tale
eccezione in maniera appropriata.
Ridefinendo A::f() con B::f(), la quale a dispetto del nome potrebbe fare
qualcosa di profondamente diverso, perche' obbligarmi a essere vincolato a
mantenere inviolato il tipo di eccezioni sollevabili?
Grazie

 offre?
In realta' la domanda che ci si dovrebbe porre e': le exception
specifications, che vantaggi offrono? E' opinione diffusa che non ne
offrano alcuno, vedi ad esempio 
http://www.gotw.ca/gotw/082.htm
Ciao,
Nicola

offrano alcuno, vedi ad esempio 
http://www.gotw.ca/gotw/082.htm
Beh, mi sembra comodo poter vedere in una riga quali tipi di eccezioni può
sollevare una funzione piuttosto che andare a cercare tutti i throw nel
corpo della funzione stessa.
Ciao

Metti un commento, allora. In fase di compilazione l'exception
specification ha lo stesso effetto.
Ciao,
Nicola

 specification ha lo stesso effetto.
Si, in effetti nonostante dichiari i tipi delle eccezioni sollevabili,
compila anche se gli faccio lanciare altri tipi di eccezioni. Boh, forse il
programmatore leggendo throw(T1, T2, ...) invece del commento, presta più
attenzione :P
Ciao

 maniera appropriata.
Purtroppo non e' cosi' facile gestirle.
supponi di avere questo codice cliente:
void g(A* a) {
 ...
  try {
    a->f()
    ...
  } catch(int e) {
    // siamo sicuri che in caso di problemi
    // passiamo da qui.
   ...
  }
  // e anche da qui
  ...
}
A un certo punto, pero' supponiamo che tu scriva una class derivata B
con un  implementazione di f() che sollevi qualche altro tipo di
eccezione.
Nel momento in cui chiami g() con un istanza di B le assunzioni fatte
in g() vengono a cadere.
Specificare le eccezioni sollevate quando si dichiara una funzione fa
parte del "contratto" che si stipula tra chi fornisce un'interfaccia e
chi la usa.
Se si tratta di un progetto piccolo, puoi anche andare a modificare
tutti i client uno a uno, ma in generale e' bene rispettare il
contratto (e spesso non c'e scelta, se l'interfaccia e' usata da
altri).
Purtroppo questo sistema non e' potente come potrebbe perche' il
default per una funzione che non specifichi nulla e' che possa
sollevare qualsiasi eccezione (invece di nessuna eccezione)
Questo rende praticamente impossibile un controllo "stretto" delle
eccezioni e quindi meglio non affidarvisi e gestire le situazioni
"eccezionali" in qualche altro modo (ad esempio usando idiomi come il
RAII per essere sicuri di "ripulire" tutto in ogni caso all'uscita
delle funzioni etc.)
Fabio