runtime polymorphism c
Un estudi detallat del polimorfisme en temps d'execució en C ++.
El polimorfisme en temps d'execució també es coneix com polimorfisme dinàmic o unió tardana. En polimorfisme en temps d'execució, la trucada de funció es resol en temps d'execució.
En canvi, per compilar el temps o el polimorfisme estàtic, el compilador dedueix l'objecte en temps d'execució i, a continuació, decideix quina funció crida a unir-se a l'objecte. A C ++, el polimorfisme en temps d'execució s'implementa utilitzant mètodes de substitució.
En aquest tutorial, explorarem tot sobre el polimorfisme en temps d'execució en detall.
preguntes i respostes de l'entrevista en xarxa pdf
=> Consulteu TOTS els tutorials de C ++ aquí.
Què aprendreu:
- Substitució de funcions
- Funció virtual
- Funcionament de la taula virtual i _vptr
- Funcions virtuals pures i classe abstracta
- Destructors virtuals
- Conclusió
- Lectura recomanada
Substitució de funcions
La substitució de funcions és el mecanisme mitjançant el qual es torna a definir una funció definida a la classe base a la classe derivada. En aquest cas, diem que la funció s’anul·la a la classe derivada.
Hem de recordar que la substitució de funcions no es pot fer dins d’una classe. La funció només es substitueix a la classe derivada. Per tant, l’herència hauria d’estar present per anul·lar funcions.
La segona cosa és que la funció d’una classe base que estem substituint hauria de tenir la mateixa signatura o prototip, és a dir, hauria de tenir el mateix nom, el mateix tipus de retorn i la mateixa llista d’arguments.
Vegem un exemple que demostra la substitució del mètode.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Sortida:
Classe :: Base
Classe :: Derivat
Al programa anterior, tenim una classe base i una classe derivada. A la classe base, tenim una funció show_val que s’anul·la a la classe derivada. A la funció principal, creem un objecte cadascun de la classe Base i Derivada i anomenem la funció show_val amb cada objecte. Produeix la sortida desitjada.
L'enllaç de funcions anterior que utilitza objectes de cada classe és un exemple d'enllaç estàtic.
Ara vegem què passa quan fem servir el punter de classe base i assignem objectes de classe derivats com a contingut.
A continuació es mostra l'exemple de programa:
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() //overridden function { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //Early Binding }
Sortida:
Classe :: Base
Ara veiem que la sortida és “Class :: Base”. Així doncs, independentment del tipus d’objecte que tingui el punter base, el programa emet el contingut de la funció de la classe del tipus punter base. En aquest cas, també es realitza un enllaç estàtic.
Per tal que la sortida del punter base, el contingut correcte i l'enllaç adequat, apostem per l'enllaç dinàmic de les funcions. Això s’aconsegueix mitjançant el mecanisme de funcions virtuals que s’explica a la següent secció.
Funció virtual
Perquè la funció anul·lada hauria d'estar lligada dinàmicament al cos de la funció, fem que la funció de classe base sigui virtual mitjançant la paraula clau 'virtual'. Aquesta funció virtual és una funció que s’anul·la a la classe derivada i el compilador realitza un enllaç dinàmic o tardà per a aquesta funció.
Ara modificem el programa anterior per incloure la paraula clau virtual de la següent manera:
#include using namespace std;. class Base { public: virtual void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //late Binding }
Sortida:
Classe :: Derivat
Així doncs, a la definició de Base de la classe anterior, vam fer que la funció show_val fos 'virtual'. Com que la funció de classe base es fa virtual, quan assignem objecte de classe derivat al punter de classe base i a la funció show_val, l'enllaç es produeix en temps d'execució.
Per tant, com que el punter de classe base conté objecte de classe derivat, el cos de la funció show_val de la classe derivada està vinculat a la funció show_val i, per tant, a la sortida.
A C ++, la funció anul·lada a la classe derivada també pot ser privada. El compilador només comprova el tipus d’objecte en temps de compilació i vincula la funció en temps d’execució, de manera que no fa cap diferència encara que la funció sigui pública o privada.
Tingueu en compte que si una funció es declara virtual a la classe base, serà virtual a totes les classes derivades.
Però fins ara no hem debatut sobre com les funcions virtuals tenen un paper determinant en la identificació de les funcions correctes que s’han d’enllaçar o, dit d’una altra manera, de com s’esdevé l’enllaç tardà.
La funció virtual s'uneix al cos de la funció amb precisió en temps d'execució mitjançant el concepte de taula virtual (VTABLE) i un punter ocult anomenat _vptr.
Tots dos conceptes són implementació interna i no poden ser utilitzats directament pel programa.
Funcionament de taula virtual i _vptr
En primer lloc, entenem què és una taula virtual (VTABLE).
El compilador en el moment de la compilació estableix una VTABLE cadascuna per a una classe que té funcions virtuals, així com les classes que es deriven de classes que tenen funcions virtuals.
Un VTABLE conté entrades que són indicadors de funcions cap a les funcions virtuals que poden ser cridades pels objectes de la classe. Hi ha una entrada de punter de funció per a cada funció virtual.
En el cas de funcions virtuals pures, aquesta entrada és NULA. (Aquesta és la raó per la qual no podem instanciar la classe abstracta).
La següent entitat, _vptr, que s'anomena punter vtable, és un punter ocult que el compilador afegeix a la classe base. Aquest _vptr apunta al vtable de la classe. Totes les classes derivades d'aquesta classe base hereten el _vptr.
Tots els objectes d'una classe que contenen les funcions virtuals emmagatzemen internament aquest _vptr i són transparents per a l'usuari. Totes les trucades a la funció virtual mitjançant un objecte es resolen mitjançant aquest _vptr.
Prenguem un exemple per demostrar el funcionament de vtable i _vtr.
#include using namespace std; class Base_virtual { public: virtual void function1_virtual() {cout<<'Base :: function1_virtual()
';}; virtual void function2_virtual() {cout<<'Base :: function2_virtual()
';}; virtual ~Base_virtual(){}; }; class Derived1_virtual: public Base_virtual { public: ~Derived1_virtual(){}; virtual void function1_virtual() { coutfunction2_virtual(); delete (b); return (0); }
Sortida:
Derived1_virtual :: function1_virtual ()
Base :: function2_virtual ()
què pot obrir un fitxer eps
Al programa anterior, tenim una classe base amb dues funcions virtuals i un destructor virtual. També hem derivat una classe de la classe base i en aquesta; només hem substituït una funció virtual. A la funció principal, el punter de classe derivat s'assigna al punter base.
A continuació, anomenem les dues funcions virtuals mitjançant un punter de classe base. Veiem que la funció anul·lada s’anomena quan es diu i no la funció base. Mentre que en el segon cas, com que la funció no s’anula, la funció de classe base s’anomena.
Ara vegem com es representa internament el programa anterior mitjançant vtable i _vptr.
Segons l'explicació anterior, ja que hi ha dues classes amb funcions virtuals, tindrem dues vtables: una per a cada classe. A més, _vptr estarà present per a la classe base.

A la part superior es mostra la representació pictòrica de com serà el disseny de la taula vtable per al programa anterior. La taula vt de la classe base és senzilla. En el cas de la classe derivada, només se substitueix function1_virtual.
Per tant, veiem que a la classe derivada vtable, el punter de funció per function1_virtual apunta a la funció anul·lada a la classe derivada. D'altra banda, el punter de funció per function2_virtual apunta a una funció de la classe base.
Així, al programa anterior, quan al punter base se li assigna un objecte de classe derivat, el punter base apunta a _vptr de la classe derivada.
Per tant, quan es fa la trucada b-> function1_virtual (), es crida la funció1_virtual de la classe derivada i quan es fa la trucada a funció b-> function2_virtual (), ja que aquest punter de funció apunta a la funció de classe base, la funció de classe base es diu.
Funcions virtuals pures i classe abstracta
Hem vist detalls sobre les funcions virtuals a C ++ a la nostra secció anterior. A C ++, també podem definir un “ pura funció virtual ”Que normalment s’equipara a zero.
La funció virtual pura es declara com es mostra a continuació.
virtual return_type function_name(arg list) = 0;
La classe que té almenys una funció virtual pura que s'anomena ' classe abstracta ”. Mai no podem instanciar la classe abstracta, és a dir, no podem crear un objecte de la classe abstracta.
Això es deu al fet que sabem que es fa una entrada per a cada funció virtual a la VTABLE (taula virtual). Però en el cas d’una funció virtual pura, aquesta entrada no té cap adreça i, per tant, la fa incompleta. Per tant, el compilador no permet crear un objecte per a la classe amb una entrada VTABLE incompleta.
Aquesta és la raó per la qual no podem instanciar una classe abstracta.
L'exemple següent mostrarà la funció virtual pura, així com la classe abstracta.
#include using namespace std; class Base_abstract { public: virtual void print() = 0; // Pure Virtual Function }; class Derived_class:public Base_abstract { public: void print() { cout <<'Overriding pure virtual function in derived class
'; } }; int main() { // Base obj; //Compile Time Error Base_abstract *b; Derived_class d; b = &d; b->print(); }
Sortida:
Substitució de la funció virtual pura a la classe derivada
Al programa anterior, tenim una classe definida com a Base_abstract que conté una funció virtual pura que el converteix en una classe abstracta. A continuació, derivem una classe 'Derived_class' de Base_abstract i anul·lem la impressió de la funció virtual pura.
A la funció principal, no es comenta la primera línia. Això es deu al fet que si el descomentem, el compilador produirà un error ja que no podem crear un objecte per a una classe abstracta.
Però a la segona línia el codi funciona. Podem crear amb èxit un punter de classe base i després li assignem objecte de classe derivat. A continuació, anomenem una funció d'impressió que genera el contingut de la funció d'impressió anul·lada a la classe derivada.
Anem a enumerar algunes característiques de la classe abstracta en breu:
- No podem instanciar una classe abstracta.
- Una classe abstracta conté almenys una funció virtual pura.
- Tot i que no podem instanciar una classe abstracta, sempre podem crear indicadors o referències a aquesta classe.
- Una classe abstracta pot tenir alguna implementació com propietats i mètodes juntament amb funcions virtuals pures.
- Quan derivem una classe de la classe abstracta, la classe derivada hauria de substituir totes les funcions virtuals pures de la classe abstracta. Si no ho va fer, la classe derivada també serà una classe abstracta.
Destructors virtuals
Els destructors de la classe es poden declarar virtuals. Sempre que fem upcast, és a dir, assignant l’objecte de classe derivat a un punter de classe base, els destructors normals poden produir resultats inacceptables.
Per exemple,tingueu en compte el següent enderrocament del destructor ordinari.
#include using namespace std; class Base { public: ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Sortida:
el millor programari espia per a mòbils Android
Classe base :: Destructor
Al programa anterior, tenim una classe derivada heretada de la classe base. En general, assignem un objecte de la classe derivada a un punter de classe base.
Idealment, el destructor que es diu quan es diu 'esborrar b' hauria d'haver estat el de classe derivada, però es pot veure des de la sortida que el destructor de la classe base s'anomena punter de classe base a això.
A causa d'això, no es crida al destructor de classes derivades i l'objecte de classe derivat roman intacte, donant lloc a una fuita de memòria. La solució a això és fer que el constructor de classes base sigui virtual perquè el punter de l’objecte apunti cap al destructor correcte i es dugui a terme la destrucció adequada dels objectes.
L'ús del destructor virtual es mostra a l'exemple següent.
#include using namespace std; class Base { public: virtual ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Sortida:
Classe derivada :: Destructor
Classe base :: Destructor
Aquest és el mateix programa que el programa anterior, tret que hem afegit una paraula clau virtual davant del destructor de la classe base. En fer virtual el destructor de classe base, hem aconseguit el resultat desitjat.
Podem veure que quan assignem objecte de classe derivat al punter de classe base i després suprimim el punter de classe base, els destructors s’anomenen en l’ordre invers de la creació d’objectes. Això vol dir que primer s’anomena el destructor de classes derivades i es destrueix l’objecte i després es destrueix l’objecte de la classe base.
Nota: A C ++, els constructors mai no poden ser virtuals, ja que els constructors participen en la construcció i inicialització dels objectes. Per tant, necessitem que tots els constructors s’executin completament.
Conclusió
El polimorfisme en temps d'execució s'implementa mitjançant la substitució del mètode. Això funciona bé quan anomenem els mètodes amb els seus respectius objectes. Però quan tenim un punter de classe base i anomenem mètodes anul·lats mitjançant el punter de classe base que apunta als objectes de classe derivats, es produeixen resultats inesperats a causa de l'enllaç estàtic.
Per superar-ho, fem servir el concepte de funcions virtuals. Amb la representació interna de vtables i _vptr, les funcions virtuals ens ajuden a anomenar amb precisió les funcions desitjades. En aquest tutorial, hem vist amb detall el polimorfisme en temps d'execució utilitzat en C ++.
Amb això, concloguem els nostres tutorials sobre programació orientada a objectes en C ++. Esperem que aquest tutorial sigui útil per obtenir una comprensió millor i completa dels conceptes de programació orientada a objectes en C ++.
=> Visiteu aquí per aprendre C ++ des de zero.
Lectura recomanada
- Polimorfisme en C ++
- Herència a C ++
- Funcions d'amistat a C ++
- Classes i objectes en C ++
- Ús de la classe Selenium Select per a la manipulació d’elements desplegables en una pàgina web - Tutorial Selenium # 13
- Tutorial de funcions principals de Python amb exemples pràctics
- Màquina virtual Java: com ajuda JVM a executar aplicacions Java
- Com configurar els fitxers de scripts de LoadRunner VuGen i la configuració del temps d'execució