2009-01-30 30 views
92

Ho una classe base con una funzione virtuale e voglio sovrascrivere quella funzione in una classe derivata. C'è un modo per far sì che il compilatore verifichi se la funzione che ho dichiarato nella classe derivata sovrascrive effettivamente una funzione nella classe base? Vorrei aggiungere qualche macro o qualcosa che assicuri che non abbia dichiarato accidentalmente una nuova funzione, invece di sovrascrivere quella vecchia.Sovrascrivere in modo sicuro le funzioni virtuali C++

Prendete questo esempio:

class parent { 
public: 
    virtual void handle_event(int something) const { 
    // boring default code 
    } 
}; 

class child : public parent { 
public: 
    virtual void handle_event(int something) { 
    // new exciting code 
    } 
}; 

int main() { 
    parent *p = new child(); 
    p->handle_event(1); 
} 

Qui parent::handle_event() è chiamato al posto di child::handle_event(), perché il metodo del bambino manca la dichiarazione di const e dichiara quindi un nuovo metodo. Questo potrebbe anche essere un refuso nel nome della funzione o qualche piccola differenza nei tipi di parametri. Può anche accadere facilmente se l'interfaccia della classe base cambia e da qualche parte una classe derivata non è stata aggiornata per riflettere la modifica.

C'è qualche modo per evitare questo problema, posso in qualche modo dire al compilatore o qualche altro strumento per controllare questo per me? Eventuali flag utili per il compilatore (preferibilmente per g ++)? Come eviti questi problemi?

+2

Grande questione, ho cercato di capire perché la mia funzione di classe figlia doesn; t ottenere chiamato in quanto un'ora ora! –

risposta

78

Dal g ++ 4.7 si capisce il nuovo C++ 11 override parola chiave:

class child : public parent { 
    public: 
     // force handle_event to override a existing function in parent 
     // error out if the function with the correct signature does not exist 
     void handle_event(int something) override; 
}; 
+0

Grazie mille per l'intuizione. –

+0

@hirschhornsalz: ho scoperto che quando si implementa la funzione handle_event e si aggiunge l'override alla fine dell'implementazione della funzione, g ++ dà un errore; se fornite un'implementazione della funzione inline nella dichiarazione della classe che segue la parola chiave override, tutto va bene. Perché? – h9uest

+3

@ h9uest 'override' deve essere utilizzato nella definizione. Un'implementazione in linea è sia la definizione che l'implementazione, quindi è ok. – hirschhornsalz

3

Suggerirei un leggero cambiamento nella tua logica. Potrebbe o meno funzionare, a seconda di ciò che è necessario realizzare.

handle_event() può ancora eseguire il "codice di default noioso" ma invece di essere virtuale, nel punto in cui si desidera eseguire il "nuovo codice eccitante", chiamare la classe base un metodo astratto (ad esempio, deve essere -override) metodo che verrà fornito dalla classe discendente.

EDIT: E se in seguito si decide che alcuni dei vostri classi discendenti fanno non necessità di fornire "nuovo codice entusiasmante", allora si può cambiare l'astratto al virtuale e fornire un'implementazione della classe base vuota di quella funzionalità "inserito" .

2

Il compilatore potrebbe avere un avviso che può generare se una funzione di classe base viene nascosta. Se lo fa, abilitalo. Ciò colpirà const scontri e differenze negli elenchi di parametri. Sfortunatamente questo non rivelerà un errore di ortografia.

Ad esempio, questo è un avviso C4263 in Microsoft Visual C++.

20

Una parola chiave come C# override non fa parte di C++.

In gcc, -Woverloaded-virtual avverte di non nascondere una funzione virtuale di classe base con una funzione con lo stesso nome ma una firma sufficientemente diversa da non sovrascriverla. Tuttavia, non ti proteggerà dal non riuscire a sovrascrivere una funzione a causa dell'errata ortografia del nome della funzione stessa.

+2

È se si utilizza Visual C++ –

+4

L'uso di Visual C++ non esegue l'override della parola chiave in C++; potrebbe significare che stai usando qualcosa che può compilare qualche codice sorgente C++ non valido, però. ;) –

+3

Il fatto che l'override non sia valido C++ indica che lo standard è errato e non Visual C++ – Jon

15

Per quanto ne so, non puoi semplicemente renderlo astratto?

class parent { 
public: 
    virtual void handle_event(int something) const = 0 { 
    // boring default code 
    } 
}; 

Ho pensato di leggere su www.parashift.com che è possibile implementare un metodo astratto. Il che ha senso per me personalmente, l'unica cosa che fa è forzare le sottoclassi a implementarlo, nessuno ha detto nulla al riguardo non avendo il permesso di avere un'implementazione stessa.

+0

Solo ora ho capito che questo funziona su più di semplici distruttori! Grande scoperta – strager

+1

Ci sono un paio di potenziali svantaggi: 1) l'altra cosa che contrassegna uno o più metodi come astratti è rendere la classe base non-istantanea, il che può essere un problema se questo non fa parte dell'uso previsto della classe. 2) la classe base potrebbe non essere tua da modificare in primo luogo. –

+3

Sono d'accordo con Michael Burr. Fare l'abstract di classe base non fa parte della domanda.È perfettamente ragionevole avere una classe base con funzionalità in un metodo virtuale, che si desideri sovrascrivere una classe derivata. Ed è altrettanto ragionevole voler proteggere contro un altro programmatore che rinomina la funzione nella classe base, e fa sì che la classe derivata non la sostituisca più. L'estensione "override" di Microsoft è inestimabile in questo caso. Mi piacerebbe vederlo aggiunto allo standard, perché purtroppo non c'è un buon modo per farlo senza. – Brian

11

In MSVC, è possibile utilizzare la parola chiave CLR override anche se non si sta compilando per CLR.

In g ++, non esiste un modo diretto per far rispettare questo in tutti i casi; altre persone hanno dato buone risposte su come rilevare le differenze di firma usando -Woverloaded-virtual.In una versione futura, qualcuno potrebbe aggiungere la sintassi come __attribute__ ((override)) o l'equivalente utilizzando la sintassi C++ 0x.

5

Rendi astratta la funzione, in modo che le classi derivate non abbiano altra scelta che sovrascriverla.

@Ray Il tuo codice non è valido.

class parent { 
public: 
    virtual void handle_event(int something) const = 0 { 
    // boring default code 
    } 
}; 

Le funzioni astratte non possono avere corpi definiti in linea. Si deve essere modificato per diventare

class parent { 
public: 
    virtual void handle_event(int something) const = 0; 
}; 

void parent::handle_event(int something) { /* do w/e you want here. */ } 
9

In MSVC++ è possibile utilizzare keyword override

 
    class child : public parent { 
    public: 
     virtual void handle_event(int something) override { 
     // new exciting code 
     } 
    }; 

override lavori sia per il codice nativo e CLR in MSVC++.

Problemi correlati