6

Si consideri il seguente codice:specifica eccezione quando l'override una funzione virtuale


class A 
{ 
public: 
    virtual void f() throw (int) { } 
}; 

class B: public A 
{ 
public: 
    void f() throw (int, double) { } 
}; 

Quando compilato, si dice che classe derivata B ha un perdente tiro identificatore rispetto ad A. Qual è l'importanza di questo? Se proviamo a scambiare le loro specifiche di eccezione, così che A :: f() genera int e double mentre B :: f() lancia solo int, l'errore non appare.

+2

Spero sinceramente che non vengano mai lanciate istanze di classi che non devono essere utilizzate come eccezioni e questo è solo per una rapida e sporca illustrazione della tua domanda :) –

+0

Matthieu: Ah, sì certo. Capisco. – jasonline

risposta

13
  1. Don't use exception specifications in C++. È molto controintuitivo rispetto a, per esempio, quelli di Java.
  2. Avere una specifica più ampia nella classe derivata interrompe LSP (Principio di sostituzione di Liskov).

Per espandere il punto 2: i chiamanti A s' si aspettano che solo int viene fuori, ma se si utilizza un B (che, perché è pubblicamente derivato da A, significa anche che è utilizzabile come A), improvvisamente double può uscire anche lui, e questo potrebbe rompere il contratto di A (che viene lanciato solo lo int).

+1

Affinché Herb Sutter interpreti questo (che è d'accordo con quello di Chris), vedere http://www.gotw.ca/publications/mill22.htm –

+1

Infatti, C++ verifica anche questo nelle dichiarazioni di funzioni implicite (per le funzioni che chiama direttamente da quelli, la loro specifica di eccezione include le specifiche della funzione chiamata): 'struct A {virtual ~ A() throw(); }; struct B {~ B() throw (int); }; struct C: A, B {};/* errore: ~ C() ha throw (int), ma ~ A() ha throw()! */' –

+0

@Neil: Grazie per il link; Ho cambiato il mio link (originariamente un GotW) per usarlo. :-) –

1

tuo B viola il principio di sostituzione di liskov - ad esempio:

 
void foo(A* a) throw() // ie I will not throw 
{ 
    try 
    { 
    a->f(); 
    } 
    catch(int) 
    {} 
} 

Questo è valida secondo l'interfaccia per A; in particolare, non prevediamo il lancio di un doppio. Ma prendere in considerazione se dovessimo chiamare

foo(new B)

con l'interfaccia che lei descrive, e

B::f()
dovesse lanciare una doppia.