Funció virtual
En la programació orientada a objectes, com s'utilitza sovint en C++ i Object Pascal, una funció virtual o mètode virtual és una funció o mètode heretable i anul·lable que s'envia dinàmicament. Les funcions virtuals són una part important del polimorfisme (en temps d'execució) en la programació orientada a objectes (OOP). Permeten l'execució de funcions de destinació que no es van identificar amb precisió en temps de compilació.
La majoria dels llenguatges de programació, com ara JavaScript, PHP i Python, tracten tots els mètodes com a virtuals per defecte [1][2] i no proporcionen un modificador per canviar aquest comportament. Tanmateix, alguns idiomes proporcionen modificadors per evitar que els mètodes siguin anul·lats per classes derivades (com ara les paraules clau finals i privades a Java [3] i PHP [4]).
Propòsit
El concepte de funció virtual resol el següent problema:
En la programació orientada a objectes, quan una classe derivada hereta d'una classe base, es pot fer referència a un objecte de la classe derivada mitjançant un punter o referència del tipus de classe base en lloc del tipus de classe derivada. Si hi ha mètodes de classe base anul·lats per la classe derivada, el mètode que realment crida aquesta referència o punter es pot lligar (enllaçar) ja sigui "primerament" (pel compilador), segons el tipus declarat del punter o referència, o bé 'tard' (és a dir, pel sistema d'execució de l'idioma), segons el tipus real de l'objecte al qual es fa referència.
Les funcions virtuals es resolen 'tard'. Si la funció en qüestió és "virtual" a la classe base, la implementació de la funció de la classe més derivada es crida segons el tipus real de l'objecte referit, independentment del tipus declarat del punter o referència. Si no és "virtual", el mètode es resol "d'hora" i es selecciona segons el tipus declarat del punter o referència.
Les funcions virtuals permeten a un programa cridar mètodes que ni tan sols existeixen necessàriament en el moment en què es compila el codi.
En C++, els mètodes virtuals es declaren anteposant la paraula clau virtual per a la declaració de la funció a la classe base. Aquest modificador és heretat per totes les implementacions d'aquest mètode a les classes derivades, el que significa que poden continuar anul·lant-se mútuament i estar lligats tard. I fins i tot si els mètodes propietat de la classe base criden al mètode virtual, en canvi cridaran al mètode derivat. La sobrecàrrega es produeix quan dos o més mètodes d'una classe tenen el mateix nom de mètode però paràmetres diferents. Anular significa tenir dos mètodes amb el mateix nom de mètode i paràmetres. La sobrecàrrega també es coneix com a concordança de funcions i la substitució com a mapeig de funcions dinàmiques.
![](http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/ClassDiagram_for_VirtualFunction.png/400px-ClassDiagram_for_VirtualFunction.png)
Exemple
C++
Per exemple, una classe base Animal
podria tenir una funció virtual Eat
. La subclasse Llama
implementaria Eat
de manera diferent que la subclasse Wolf
, però es pot invocar Eat
a qualsevol instància de classe anomenada Animal i obtenir el comportament Eat
de la subclasse específica.
class Animal { public: // Intentionally not virtual: void Move() { std::cout << "This animal moves in some way" << std::endl; } virtual void Eat() = 0; }; // The class "Animal" may possess a definition for Eat if desired. class Llama : public Animal { public: // The non virtual function Move is inherited but not overridden. void Eat() override { std::cout << "Llamas eat grass!" << std::endl; } };
Això permet que un programador processi una llista d'objectes de la classe Animal
, dient-li a cadascú que mengi (cridant Eat
), sense necessitat de saber quin tipus d'animal pot ser a la llista, com menja cada animal o quin és el conjunt complet. dels possibles tipus d'animals podrien ser. A C, el mecanisme darrere de les funcions virtuals es podria proporcionar de la manera següent:
#include <stdio.h> /* an object points to its class... */ struct Animal { const struct AnimalClass * class; }; /* which contains the virtual function Animal.Eat */ struct AnimalClass { void (*Eat)(struct Animal *); // 'virtual' function }; /* Since Animal.Move is not a virtual function it is not in the structure above. */ void Move(struct Animal * self) { printf("<Animal at %p> moved in some way\n", (void *) self); } /* unlike Move, which executes Animal.Move directly, Eat cannot know which function (if any) to call at compile time. Animal.Eat can only be resolved at run time when Eat is called. */ void Eat(struct Animal * self) { const struct AnimalClass * class = *(const void **) self; if (class->Eat) class->Eat(self); // execute Animal.Eat else fprintf(stderr, "Eat not implemented\n"); } /* implementation of Llama.Eat this is the target function to be called by 'void Eat(struct Animal *).' */ static void _Llama_eat(struct Animal * self) { printf("<Llama at %p> Llama's eat grass!\n", (void *) self); } /* initialize class */ const struct AnimalClass Animal = {(void *) 0}; // base class does not implement Animal.Eat const struct AnimalClass Llama = {_Llama_eat}; // but the derived class does int main(void) { /* init objects as instance of its class */ struct Animal animal = {& Animal}; struct Animal llama = {& Llama}; Move(& animal); // Animal.Move Move(& llama); // Llama.Move Eat(& animal); // cannot resolve Animal.Eat so print "Not Implemented" to stderr Eat(& llama); // resolves Llama.Eat and executes }
Referències
- ↑ «Polymorphism (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance)» (en anglès). docs.oracle.com. [Consulta: 11 juliol 2020].
- ↑ «9. Classes — Python 3.9.2 documentation» (en anglès). docs.python.org. [Consulta: 23 febrer 2021].
- ↑ «Writing Final Classes and Methods (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance)» (en anglès). docs.oracle.com. [Consulta: 11 juliol 2020].
- ↑ «PHP: Final Keyword - Manual» (en anglès). www.php.net. [Consulta: 11 juliol 2020].