Una funzione virtuale è una funzione, definita in una superclasse, che deve essere presente in una sottoclasse affinché quella sottoclasse abbia una definizione di classe completa. Le funzioni virtuali si basano su un paradigma di programmazione orientato agli oggetti chiamato ereditarietà virtuale, che è più comunemente visto in C++ utilizzando la parola chiave “virtuale”. Per definire una funzione virtuale sono necessarie due classi, una superclasse e una sottoclasse. La superclasse è dove la funzione viene dichiarata per prima e possibilmente definita. La sottoclasse è dove viene definita la funzione o sovrascritta, a seconda che la funzione sia stata definita nella superclasse.
La funzione virtuale può essere definita in due modi. Innanzitutto, può essere definito come uno stub, in cui ha un corpo vuoto e non fa nulla. In secondo luogo, potrebbe essere definita come una pura funzione virtuale, dove è definita come NULL nel file di intestazione della superclasse.
Ci sono vantaggi e svantaggi in entrambe le metodologie. Definire una funzione come stub garantisce che tutte le sottoclassi ne abbiano una qualche implementazione, anche se non fa nulla. Se si dimentica di sovrascrivere la funzione e implementarla correttamente in una sottoclasse, tuttavia, non verranno visualizzati errori o avvisi per segnalarlo. La definizione di una funzione virtuale pura, d’altra parte, richiede che ogni sottoclasse abbia una propria definizione della funzione e, in caso contrario, verranno visualizzati degli errori.
Le funzioni virtuali, tuttavia, sono soggette alle stesse regole di ereditarietà delle funzioni non virtuali, quindi le gerarchie di ereditarietà con più di due livelli potrebbero non richiedere definizioni esplicite di funzioni virtuali. Ad esempio, si può considerare una classe A che dichiara una funzione virtuale, implementata nella sottoclasse B. La classe B ha una sua sottoclasse, la classe C. La classe C non richiede una definizione esplicita della funzione della classe A, perché eredita la definizione dalla classe B. Se necessario, la classe C può sovrascrivere la funzione della classe B, oppure può sovrascrivere la funzione della classe B mentre la chiama.
All’altro estremo, le funzioni virtuali non devono essere definite in una sottoclasse se sono dichiarate virtuali in quella sottoclasse. Ad esempio, si può considerare una classe A che dichiara una funzione virtuale e ha due sottoclassi, B e C. Inoltre, si potrebbe immaginare che la classe B abbia le sottoclassi D ed E e la sottoclasse C abbia le sottoclassi F e G.
Tutte le classi da B a G devono avere la funzione virtuale della classe A definita in qualche modo. Se la classe B ha un’implementazione della funzione di A, le classi D ed E non hanno bisogno che venga rifatta. Forse le sottoclassi di C devono implementare la funzione di A, ma entrambe fanno qualcosa di diverso, quindi definire la funzione nella stessa classe C non sarebbe utile. In tal caso, la funzione può essere dichiarata virtuale nella classe C e non è necessaria un’implementazione.
Le funzioni virtuali possono essere scoraggianti da imparare, ma se usate correttamente, possono ridurre la duplicazione del codice e rendere il codice molto più facile da capire in generale. Ci sono molte insidie con le funzioni virtuali, tuttavia, soprattutto per quanto riguarda l’ereditarietà multipla. Nell’ereditarietà multipla, è possibile che funzioni virtuali definite in modo ambiguo entrino in conflitto tra loro, quindi dovrebbero essere usate con cautela in quel contesto.