runtime polymorphism c
Detaljna studija polimorfizma izvođenja u C ++.
Izvođački polimorfizam poznat je i kao dinamički polimorfizam ili kasno vezanje. U polimorfizmu izvođenja, poziv funkcije se rješava u vrijeme izvođenja.
Suprotno tome, za kompajliranje vremena ili statičkog polimorfizma, kompajler izvodi objekt u vrijeme izvođenja, a zatim odlučuje koji će se poziv funkcije vezati za objekt. U C ++-u se polimorfizam izvođenja implementira korištenjem nadjačavanja metode.
U ovom uputstvu detaljno ćemo istražiti sve o runtim polimorfizmu.
=> Ovdje provjerite SVE tutorijale za C ++.
Što ćete naučiti:
- Nadjačavanje funkcije
- Virtualna funkcija
- Rad virtualne tablice i _vptr
- Čiste virtualne funkcije i sažetak klase
- Virtualni destruktori
- Zaključak
- Preporučena literatura
Nadjačavanje funkcije
Nadjačavanje funkcije je mehanizam pomoću kojeg se funkcija definirana u osnovnoj klasi ponovno definira u izvedenoj klasi. U ovom slučaju kažemo da je funkcija nadjačana u izvedenoj klasi.
Trebali bismo imati na umu da se nadjačavanje funkcije ne može izvršiti unutar klase. Funkcija je nadjačana samo u izvedenoj klasi. Stoga bi nasljeđivanje trebalo biti prisutno za nadjačavanje funkcije.
Druga stvar je da bi funkcija iz osnovne klase koju nadjačavamo trebala imati isti potpis ili prototip, tj. Trebala bi imati isto ime, isti tip povratka i isti popis argumenata.
Pogledajmo primjer koji pokazuje nadjačavanje metode.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Izlaz:
Razred :: Baza
Razred :: Izvedeno
U gornjem programu imamo osnovnu i izvedenu klasu. U osnovnoj klasi imamo funkciju show_val koja je nadjačana u izvedenoj klasi. U glavnoj funkciji stvaramo svaki objekt klase Base i Derived i pozivamo funkciju show_val sa svakim objektom. Proizvodi željeni izlaz.
Gornje vezivanje funkcija pomoću objekata svake klase primjer je statičkog vezivanja.
Sada da vidimo što se događa kada koristimo pokazivač osnovne klase i dodamo izvedene objekte klase kao njegov sadržaj.
Primjer programa prikazan je u nastavku:
#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 }
Izlaz:
Razred :: Baza
Sada vidimo, da je izlaz 'Class :: Base'. Dakle, neovisno o tome koji tip objekta drži osnovni pokazivač, program prikazuje sadržaj funkcije klase čiji je osnovni pokazivač tip. U tom se slučaju provodi i statičko povezivanje.
Da bi osnovni pokazivač bio izlaz, ispravan sadržaj i pravilno povezivanje, idemo na dinamičko vezivanje funkcija. To se postiže pomoću mehanizma virtualnih funkcija koji je objašnjen u sljedećem odjeljku.
Virtualna funkcija
Kako bi nadjačana funkcija trebala biti dinamički vezana za tijelo funkcije, funkciju osnovne klase učinimo virtualnom pomoću ključne riječi 'virtual'. Ova virtualna funkcija je funkcija koja je nadjačana u izvedenoj klasi i prevodilac za nju vrši kasno ili dinamičko vezanje.
Sada izmijenimo gornji program tako da uključuje virtualnu ključnu riječ na sljedeći način:
#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 }
Izlaz:
kako stvoriti graf u javi
Razred :: Izvedeno
Tako smo u gornjoj definiciji klase Base napravili show_val funkciju kao 'virtualnu'. Kako se funkcija osnovne klase čini virtualnom, kada dodijelimo objekt izvedene klase pokazivaču osnovne klase i pozivamo funkciju show_val, vezivanje se događa u vrijeme izvođenja.
Prema tome, kako pokazivač osnovne klase sadrži izvedeni objekt klase, tijelo funkcije show_val u izvedenoj klasi vezano je za funkciju show_val, a time i na izlaz.
U C ++-u nadjačana funkcija u izvedenoj klasi također može biti privatna. Prevoditelj provjerava vrstu objekta samo u vrijeme sastavljanja i veže funkciju u vrijeme izvođenja, stoga ne čini nikakvu razliku čak i ako je funkcija javna ili privatna.
Imajte na umu da ako je funkcija proglašena virtualnom u osnovnoj klasi, tada će biti virtualna u svim izvedenim razredima.
Ali do sada nismo raspravljali o tome kako točno virtualne funkcije igraju ulogu u identificiranju ispravne funkcije koja se veže ili drugim riječima, kako se zapravo događa kasno vezivanje.
Virtualna je funkcija točno povezana s tijelom funkcije tijekom izvođenja pomoću koncepta virtualna tablica (VTABLE) i skriveni pokazivač zvan _vptr.
Oba su koncepta interna implementacija i program ih ne može izravno koristiti.
Rad virtualne tablice i _vptr
Prvo, shvatimo što je virtualna tablica (VTABLE).
Kompajler u vrijeme kompajliranja postavlja po jedan VTABLE za klasu koja ima virtualne funkcije, kao i za klase izvedene iz klasa koje imaju virtualne funkcije.
VTABLE sadrži unose koji su pokazivači funkcija na virtualne funkcije koje mogu pozvati objekti klase. Za svaku virtualnu funkciju postoji jedan unos pokazivača funkcije.
otvaranje eps datoteke u sustavu Windows
U slučaju čistih virtualnih funkcija, ovaj je unos NULL. (To je razlog zašto ne možemo napraviti instancu apstraktne klase).
Sljedeći entitet, _vptr koji se naziva pokazivač vtable, skriveni je pokazivač koji kompajler dodaje osnovnoj klasi. Ovaj _vptr ukazuje na vtable klase. Sve klase izvedene iz ove osnovne klase nasljeđuju _vptr.
Svaki objekt klase koji sadrži virtualne funkcije interno pohranjuje ovaj _vptr i pregledan je za korisnika. Svaki poziv virtualnoj funkciji pomoću objekta rješava se pomoću ovog _vptr.
Uzmimo primjer da pokažemo kako rade 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); }
Izlaz:
Izvedeno1_virtual :: function1_virtual ()
Baza :: function2_virtual ()
U gore navedenom programu imamo osnovnu klasu s dvije virtualne funkcije i virtualnim destruktorom. Također smo izveli klasu iz osnovne klase i u tome; nadjačali smo samo jednu virtualnu funkciju. U glavnoj funkciji izvedeni pokazivač klase dodjeljuje se osnovnom pokazivaču.
Tada pozivamo obje virtualne funkcije pomoću pokazivača osnovne klase. Vidimo da se nadjačana funkcija poziva kad se pozove, a ne osnovna funkcija. Dok se u drugom slučaju, budući da funkcija nije nadjačana, poziva funkcija osnovne klase.
Sada da vidimo kako je gornji program interno predstavljen pomoću vtable i _vptr.
Prema ranijem objašnjenju, budući da postoje dvije klase s virtualnim funkcijama, imat ćemo dvije vtable - po jednu za svaku klasu. Također, _vptr će biti prisutan za osnovnu klasu.

Iznad je prikazan slikovni prikaz izgleda vtable izgleda za gornji program. Vtable za osnovnu klasu je jednostavan. U slučaju izvedene klase, poništava se samo function1_virtual.
Stoga vidimo da u izvedenoj klasi vtable, pokazivač funkcije za function1_virtual ukazuje na nadjačanu funkciju u izvedenoj klasi. S druge strane, pokazivač funkcije za function2_virtual usmjerava na funkciju u osnovnoj klasi.
Dakle, u gornjem programu kada je osnovnom pokazivaču dodijeljen izvedeni objekt klase, osnovni pokazivač pokazuje na _vptr izvedene klase.
Dakle, kada se izvrši poziv b-> function1_virtual (), poziva se function1_virtual iz izvedene klase i kada se izvrši poziv funkcije b-> function2_virtual (), jer ovaj pokazivač funkcije pokazuje na funkciju osnovne klase, funkciju osnovne klase Zove se.
Čiste virtualne funkcije i sažetak klase
Pojedinosti o virtualnim funkcijama u C ++-u vidjeli smo u našem prethodnom odjeljku. U C ++-u također možemo definirati ' čista virtualna funkcija ”Koji se obično izjednačava s nulom.
Čista virtualna funkcija deklarirana je kao što je prikazano u nastavku.
virtual return_type function_name(arg list) = 0;
Klasa koja ima barem jednu čistu virtualnu funkciju koja se naziva „ apstraktni razred '. Nikada ne možemo instancirati apstraktnu klasu, tj. Ne možemo stvoriti objekt apstraktne klase.
To je zato što znamo da se unos vrši za svaku virtualnu funkciju u VTABLE-u (virtualna tablica). Ali u slučaju čisto virtualne funkcije, ovaj je unos bez adrese, što ga čini nepotpunim. Dakle, kompajler ne dopušta stvaranje objekta za klasu s nepotpunim unosom VTABLE.
To je razlog zbog kojeg ne možemo stvoriti instancu apstraktne klase.
Sljedeći primjer pokazat će čistu virtualnu funkciju kao i klasu Sažetak.
#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(); }
Izlaz:
Nadjačavanje čiste virtualne funkcije u izvedenoj klasi
U gornjem programu imamo klasu definiranu kao Base_abstract koja sadrži čistu virtualnu funkciju što je čini apstraktnom klasom. Zatim izvedemo klasu “Izvedena_klasa” iz Base_abstract i nadjačamo čisti virtualni ispis funkcije u njoj.
U glavnoj se funkciji ne komentira taj prvi redak. To je zato što ako ga komentiramo, kompajler će dati pogrešku jer ne možemo stvoriti objekt za apstraktnu klasu.
Ali drugi redak dalje kod radi. Možemo uspješno stvoriti pokazivač osnovne klase i tada mu dodijelimo izvedeni objekt klase. Dalje, nazivamo funkciju ispisa koja izbacuje sadržaj funkcije ispisa nadjačan u izvedenoj klasi.
Navedimo ukratko neke karakteristike apstraktne nastave:
kako pregledati .dat datoteku
- Ne možemo instancirati apstraktnu klasu.
- Apstraktna klasa sadrži barem jednu čistu virtualnu funkciju.
- Iako ne možemo instancirati apstraktnu klasu, uvijek možemo stvoriti pokazivače ili reference na ovu klasu.
- Apstraktna klasa može imati neke implementacije poput svojstava i metoda, zajedno s čistim virtualnim funkcijama.
- Kada izvodimo klasu iz apstraktne klase, izvedena klasa trebala bi nadjačati sve čiste virtualne funkcije u apstraktnoj klasi. Ako to nije uspio, izvedena klasa također će biti apstraktna klasa.
Virtualni destruktori
Destruktori klase mogu se deklarirati kao virtualni. Kad god napravimo upcast, tj. Dodijelimo izvedeni objekt klase pokazivaču osnovne klase, obični destruktori mogu proizvesti neprihvatljive rezultate.
Na primjer,razmotrite sljedeće nadogradnju uobičajenog destruktora.
#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; }
Izlaz:
Osnovna klasa :: Destruktor
U gornjem programu imamo naslijeđenu izvedenu klasu iz osnovne klase. Uglavnom dodijeljujemo objekt izvedene klase pokazivaču osnovne klase.
U idealnom slučaju, destruktor koji se poziva kada se zove 'delete b' trebao je biti izvedene klase, ali iz rezultata možemo vidjeti da se destruktor osnovne klase naziva dok pokazivač osnovne klase ukazuje na to.
Zbog toga se izvedeni destruktor klase ne poziva i izvedeni objekt klase ostaje netaknut, što rezultira curenjem memorije. Rješenje za to je učiniti konstruktor osnovne klase virtualnim tako da pokazivač objekta pokazuje ispravni destruktor i provodi se pravilno uništavanje objekata.
Korištenje virtualnog destruktora prikazano je u donjem primjeru.
#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; }
Izlaz:
Izvedena klasa :: Destructor
Osnovna klasa :: Destruktor
Ovo je isti program kao i prethodni program, osim što smo dodali virtualnu ključnu riječ ispred destruktora osnovne klase. Čineći destruktor osnovne klase virtualnim, postigli smo željeni izlaz.
Možemo vidjeti da kada dodijelimo izvedeni objekt klase pokazivaču osnovne klase, a zatim izbrišemo pokazivač osnovne klase, destruktori se pozivaju obrnutim redoslijedom stvaranja objekta. To znači da se prvo poziva izvedeni destruktor klase i objekt se uništava, a zatim se uništava objekt osnovne klase.
Bilješka: U C ++-u konstruktori nikada ne mogu biti virtualni, jer su konstruktori uključeni u konstrukciju i inicijalizaciju objekata. Stoga trebamo da se svi konstruktori izvrše u potpunosti.
Zaključak
Izvršni polimorfizam provodi se korištenjem nadjačavanja metode. To dobro funkcionira kada metode pozivamo s njihovim objektima. Ali kada imamo pokazivač osnovne klase i pozivamo nadjačane metode pomoću pokazivača osnovne klase koji upućuje na izvedene objekte klase, dolazi do neočekivanih rezultata zbog statičkog povezivanja.
Da bismo to prevladali, koristimo koncept virtualnih funkcija. S unutarnjim predstavljanjem vtables i _vptr, virtualne funkcije pomažu nam u preciznom pozivanju željenih funkcija. U ovom uputstvu detaljno smo vidjeli polimorfizam vremena izvođenja koji se koristi u C ++.
Ovim zaključujemo naše vodiče o objektno orijentiranom programiranju na C ++. Nadamo se da će ovaj vodič biti koristan za bolje i temeljitije razumijevanje objektno-orijentiranih koncepata programiranja na C ++.
=> Posjetite ovdje da biste C ++ naučili od nule.
Preporučena literatura
- Polimorfizam u C ++
- Nasljeđivanje u C ++
- Funkcije prijatelja u C ++
- Razredi i objekti u C ++
- Korištenje selenijske klase za rukovanje padajućim elementima na web stranici - Vodič za selenij br. 13
- Vodič za glavne funkcije Pythona s praktičnim primjerima
- Java virtualni stroj: kako JVM pomaže u pokretanju Java aplikacije
- Kako postaviti datoteke skripti LoadRunner VuGen i postavke runtimea