Storia della domanda:
In C++ è stato introdotto il supporto per la programmazione orientata agli oggetti, fondamentale per i linguaggi moderni. Le funzioni virtuali sono state utilizzate per implementare il polimorfismo. Questo consentiva di chiamare l'implementazione corretta del metodo al momento dell'esecuzione, e non solo durante la compilazione, il che è critico per un'architettura basata sull'ereditarietà.
Problema:
Un errore comune è la confusione tra la chiamata statica e dinamica dei metodi, i distruttori virtuali dimenticati, un uso errato dell'ereditarietà (ad esempio, oggetto slicing, chiamata alla versione base invece di override). Spesso si confonde quando il polimorfismo funziona realmente.
Soluzione:
Una funzione virtuale viene dichiarata usando la parola chiave virtual nella classe base e può essere sovrascritta (override) nella derivata. Se si chiama una funzione tramite un puntatore o un riferimento alla classe base, verrà eseguita la versione della classe derivata.
Esempio di codice:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Mostrerà Derived::foo }
Caratteristiche chiave:
Il polimorfismo funziona quando si passa un oggetto per valore?
No. Il passaggio per valore porta a "slicing" — viene copiata solo la parte corrispondente al tipo del parametro (di solito la classe base), il polimorfismo si disabilita.
Esempio di codice:
void call(Base b) { b.foo(); } // sempre chiamata a Base::foo
È necessario dichiarare il distruttore come virtuale nella classe base?
Sì, se si prevede di eliminare oggetti derivati tramite un puntatore alla classe base. Altrimenti, ci sarà una perdita di memoria o risorse non chiuse.
Esempio di codice:
struct Base { virtual ~Base() {} };
Cosa succede se non si utilizza la parola chiave override nella classe derivata?
Se nella classe derivata non viene specificato override, ma si modifica erroneamente la firma della funzione (ad esempio, si omette const o si sbaglia nei parametri), la funzione non sovrascrive quella virtuale, ne viene creata una nuova e il polimorfismo non funziona come previsto.
Un programmatore non ha dichiarato il distruttore della classe base come virtuale; la cancellazione di un array di oggetti tramite un puntatore base ha portato a una perdita di memoria.
Vantaggi:
Svantaggi:
È stato dichiarato un distruttore virtuale; sono stati utilizzati solo riferimenti/puntatori al tipo base. Il polimorfismo ha funzionato correttamente.
Vantaggi:
Svantaggi: