runtime polymorphism c
Podrobna študija polimorfizma med izvajanjem v jeziku C ++.
Polimorfizem med izvajanjem je znan tudi kot dinamični polimorfizem ali pozna vezava. V polimorfizmu med izvajanjem se klic funkcije razreši med izvajanjem.
Nasprotno pa prevajalnik za čas prevajanja ali statičnega polimorfizma objekt izračuna med izvajanjem in nato odloči, kateri klic funkcije naj se veže na objekt. V jeziku C ++ se polimorfizem med izvajanjem izvaja s preglasitvijo metode.
V tej vadnici bomo podrobno raziskali vse o polimorfizmu med izvajanjem.
=> Tukaj preverite VSE Vadnice za C ++.
Kaj se boste naučili:
- Preglasitev funkcije
- Navidezna funkcija
- Delo navidezne tabele in _vptr
- Čiste navidezne funkcije in abstraktni razred
- Navidezni destruktorji
- Zaključek
- Priporočeno branje
Preglasitev funkcije
Preglasitev funkcije je mehanizem, s katerim je funkcija, določena v osnovnem razredu, znova definirana v izpeljanem razredu. V tem primeru pravimo, da je funkcija v izpeljanem razredu preglašena.
Ne smemo pozabiti, da preglasitve funkcije ni mogoče izvesti znotraj razreda. Funkcija je razveljavljena samo v izpeljanem razredu. Zato mora biti dedovanje prisotno za preglasitev funkcije.
Druga stvar je, da mora imeti funkcija iz osnovnega razreda, ki ga preglasimo, enak podpis ali prototip, torej mora imeti isto ime, isti tip vrnitve in enak seznam argumentov.
Oglejmo si primer, ki dokazuje preglasitev metode.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Izhod:
Razred :: Baza
Razred :: Izvedeno
V zgornjem programu imamo osnovni razred in izpeljani razred. V osnovnem razredu imamo funkcijo show_val, ki je v izpeljanem razredu preglašena. V glavni funkciji izdelamo vsak objekt iz osnovnega in izpeljanega razreda ter z vsakim predmetom pokličemo funkcijo show_val. Ustvari želeni izhod.
Zgornja vezava funkcij, ki uporabljajo predmete vsakega razreda, je primer statične vezave.
kateri je najboljši program za posodabljanje gonilnikov
Zdaj pa poglejmo, kaj se zgodi, ko uporabimo kazalec osnovnega razreda in kot njegovo vsebino dodelimo izpeljane predmete razreda.
Primer programa je prikazan spodaj:
#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 }
Izhod:
Razred :: Baza
Zdaj vidimo, da je rezultat “Class :: Base”. Torej, ne glede na objekt tipa, ki ga ima osnovni kazalec, program prikaže vsebino funkcije razreda, katerega osnovni kazalec je tip. V tem primeru se izvede tudi statično povezovanje.
Da bi osnovni kazalec izpisal, popravil vsebino in pravilno povezal, se zavzemamo za dinamično vezavo funkcij. To dosežemo z uporabo mehanizma navideznih funkcij, ki je razložen v naslednjem poglavju.
Navidezna funkcija
Ker bi morala biti razveljavljena funkcija dinamično vezana na telo funkcije, naredimo funkcijo osnovnega razreda navidezno z uporabo ključne besede 'virtual'. Ta navidezna funkcija je funkcija, ki jo v izpeljanem razredu preglasi in prevajalnik za to funkcijo izvede pozno ali dinamično vezavo.
Zdaj pa spremenimo zgornji program tako, da vključuje virtualno ključno besedo, kot sledi:
#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 }
Izhod:
Razred :: Izvedeno
Torej, v zgornji definiciji razreda Base smo funkcijo show_val naredili kot 'virtualno'. Ker je funkcija osnovnega razreda navidezna, ko dodelimo predmet izpeljanega razreda kazalcu osnovnega razreda in pokličemo funkcijo show_val, se vezava zgodi med izvajanjem.
najboljša brezplačna programska oprema za prenos youtube videoposnetkov
Ker torej kazalec osnovnega razreda vsebuje izpeljani objekt razreda, je telo funkcije show_val v izpeljanem razredu vezano na funkcijo show_val in s tem na izhod.
V jeziku C ++ je lahko nadomeščena funkcija v izpeljanem razredu tudi zasebna. Prevajalnik preveri vrsto predmeta samo v času prevajanja in funkcijo veže med izvajanjem, zato ne vpliva, tudi če je funkcija javna ali zasebna.
Upoštevajte, da če je funkcija razglašena za navidezno v osnovnem razredu, bo navidezna v vseh izvedenih razredih.
Toda do zdaj nismo razpravljali o tem, kako natančno navidezne funkcije igrajo vlogo pri določanju pravilne funkcije, ki jo je treba vezati, ali z drugimi besedami, kako dejansko se zgodi pozna vezava.
Navidezna funkcija je med izvajanjem natančno vezana na telo funkcije s pomočjo koncepta navidezna tabela (VTABLE) in skrit kazalec, imenovan _vptr.
Oba koncepta sta notranja izvedba in jih program ne more uporabljati neposredno.
Delo navidezne tabele in _vptr
Najprej naj razumemo, kaj je navidezna tabela (VTABLE).
Prevajalnik v času prevajanja nastavi po eno VTABLE za razred z navideznimi funkcijami in razrede, ki izhajajo iz razredov z navideznimi funkcijami.
VTABLE vsebuje vnose, ki so kazalci funkcij na navidezne funkcije, ki jih lahko pokličejo predmeti razreda. Za vsako navidezno funkcijo obstaja en vnos kazalca funkcije.
V primeru navideznih funkcij je ta vnos NULL. (To je razlog, zakaj abstraktnega razreda ne moremo ustvariti).
Naslednja entiteta, _vptr, ki se imenuje kazalec vtable, je skriti kazalec, ki ga prevajalnik doda osnovnemu razredu. Ta _vptr kaže na vtable razreda. Vsi razredi, ki izhajajo iz tega osnovnega razreda, podedujejo _vptr.
Vsak predmet razreda, ki vsebuje navidezne funkcije, interno shrani ta _vptr in je uporabniku pregleden. Vsak klic navidezne funkcije z uporabo predmeta se nato razreši s tem _vptr.
Vzemimo primer za prikaz delovanja vtable in _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); }
Izhod:
Izvedeno1_virtual :: function1_virtual ()
Base :: function2_virtual ()
V zgornjem programu imamo osnovni razred z dvema navideznima funkcijama in navideznim destruktorjem. Iz osnovnega razreda smo izpeljali tudi razred in v tem; preglasili smo samo eno navidezno funkcijo. V glavni funkciji je izpeljani kazalnik razreda dodeljen osnovnemu kazalcu.
Nato pokličemo obe navidezni funkciji s kazalcem osnovnega razreda. Vidimo, da se nadrejena funkcija pokliče, ko jo pokliče, in ne osnovna funkcija. Medtem ko se v drugem primeru, ker funkcija ni razveljavljena, pokliče funkcija osnovnega razreda.
Zdaj pa poglejmo, kako je zgornji program interno predstavljen z uporabo vtable in _vptr.
Glede na prejšnjo razlago, ker obstajata dva razreda z navideznimi funkcijami, bomo imeli dve vtabeli - po eno za vsak razred. _Vptr bo prisoten tudi za osnovni razred.
Zgoraj je prikazana slikovna predstavitev, kako bo postavitev vtable za zgornji program. Vtable za osnovni razred je enostaven. V primeru izpeljanega razreda se preglasi samo function1_virtual.
Zato vidimo, da v izpeljanem razredu vtable kazalnik funkcije za function1_virtual kaže na razveljavljeno funkcijo v izpeljanem razredu. Po drugi strani pa kazalnik funkcije za function2_virtual kaže na funkcijo v osnovnem razredu.
Tako je v zgornjem programu, ko je osnovnemu kazalcu dodeljen objekt izpeljanega razreda, osnovni kazalec kaže na _vptr izpeljanega razreda.
Torej, ko se izvede klic b-> function1_virtual (), se pokliče function1_virtual iz izvedenega razreda in ko se izvede klic funkcije b-> function2_virtual (), ko ta kazalnik funkcije kaže na funkcijo osnovnega razreda, funkcijo osnovnega razreda je poklican.
Čiste navidezne funkcije in abstraktni razred
Podrobnosti o navideznih funkcijah v jeziku C ++ smo videli v prejšnjem poglavju. V jeziku C ++ lahko določimo tudi čista navidezna funkcija ', Ki je običajno enak nič.
Čista navidezna funkcija je razglašena, kot je prikazano spodaj.
virtual return_type function_name(arg list) = 0;
Razred, ki ima vsaj eno čisto navidezno funkcijo, ki se imenuje abstraktni razred '. Nikoli ne moremo ustvariti primerka abstraktnega razreda, torej ne moremo ustvariti predmeta abstraktnega razreda.
To je zato, ker vemo, da je vnos narejen za vsako navidezno funkcijo v VTABLE (navidezna tabela). Toda v primeru navidezne funkcije je ta vnos brez naslova, zaradi česar je nepopoln. Torej prevajalnik ne dovoli ustvarjanja predmeta za razred z nepopolnim vnosom VTABLE.
To je razlog, zaradi katerega abstraktnega razreda ne moremo ustvariti.
Spodnji primer prikazuje Pure virtualno funkcijo in razred Abstract.
#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(); }
Izhod:
Preglasitev čiste navidezne funkcije v izvedenem razredu
V zgornjem programu imamo razred, opredeljen kot Base_abstract, ki vsebuje čisto navidezno funkcijo, zaradi česar je abstrakten razred. Nato iz Base_abstract izpeljemo razred »Izvedeni_razred« in preglasimo tiskanje čiste navidezne funkcije v njem.
V glavni funkciji ni komentirana prva vrstica. To je zato, ker če ga komentiramo, bo prevajalnik dal napako, saj ne moremo ustvariti predmeta za abstraktni razred.
Toda druga vrstica naprej koda deluje. Kazalec osnovnega razreda lahko uspešno izdelamo in mu nato dodelimo objekt izpeljanega razreda. Nato pokličemo tiskalno funkcijo, ki izpiše vsebino tiskalne funkcije, razveljavljene v izpeljanem razredu.
Na kratko navedemo nekaj značilnosti abstraktnega predavanja:
- Ne moremo ustvariti primerka abstraktnega razreda.
- Abstraktni razred vsebuje vsaj eno čisto navidezno funkcijo.
- Čeprav abstraktnega razreda ne moremo ustvariti, lahko vedno ustvarimo kazalce ali sklice na ta razred.
- Abstraktni razred ima lahko nekaj izvedb, kot so lastnosti in metode, skupaj s čisto navideznimi funkcijami.
- Ko iz abstraktnega razreda izpeljemo razred, bi moral izpeljani razred preglasiti vse čiste navidezne funkcije v abstraktnem razredu. Če tega ni storil, bo izpeljani razred hkrati tudi abstraktni razred.
Navidezni destruktorji
Destruktorje razreda lahko razglasimo kot virtualne. Kadarkoli izvedemo upcast, tj. Dodelitev predmeta izpeljanega razreda kazalcu osnovnega razreda, lahko običajni destruktorji dajo nesprejemljive rezultate.
Na primer,razmislite o nadaljnjem posodabljanju običajnega destruktorja.
#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; }
Izhod:
Osnovni razred :: Destructor
V zgornjem programu imamo podedovan izpeljani razred iz osnovnega razreda. Kazalcu osnovnega razreda v glavnem dodelimo objekt izpeljanega razreda.
V idealnem primeru bi moral biti destruktor, ki se pokliče, ko se pokliče 'delete b', izpeljanega razreda, vendar iz izhoda lahko vidimo, da je destruktor osnovnega razreda poklican kot kazalec osnovnega razreda.
Zaradi tega izpeljani destruktor razreda ni poklican in izpeljani objekt razreda ostane nedotaknjen, kar povzroči uhajanje pomnilnika. Rešitev za to je narediti konstruktor osnovnega razreda virtualnim, tako da kazalec predmeta kaže na pravilen destruktor in se izvede pravilno uničenje predmetov.
Uporaba navideznega destruktorja je prikazana v spodnjem primeru.
#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; }
Izhod:
je omrežni ključ geslo za wifi
Izpeljani razred :: Destructor
Osnovni razred :: Destructor
To je isti program kot prejšnji program, le da smo pred destruktor osnovnega razreda dodali navidezno ključno besedo. Z izdelavo virtualnega destruktorja osnovnega razreda smo dosegli želeni izhod.
Vidimo lahko, da ko kazalcu osnovnega razreda dodelimo objekt izpeljanega razreda in nato izbrišemo kazalec osnovnega razreda, se destruktorji pokličejo v obratnem vrstnem redu ustvarjanja predmeta. To pomeni, da se najprej pokliče izpeljani destruktor razreda, objekt se uniči in nato uniči objekt osnovnega razreda.
Opomba: V jeziku C ++ konstruktorji nikoli ne morejo biti virtualni, saj konstruktorji sodelujejo pri konstruiranju in inicializaciji predmetov. Zato moramo vse konstruktorje izvesti v celoti.
Zaključek
Polimorfizem med izvajanjem se izvaja z uporabo preglasitve metode. To deluje v redu, ko metode pokličemo z njihovimi predmeti. Toda, ko imamo kazalec osnovnega razreda in pokličemo preglašene metode z uporabo kazalca osnovnega razreda, ki kaže na izpeljane predmete razreda, pride do nepričakovanih rezultatov zaradi statične povezave.
Da bi to premagali, uporabljamo koncept navideznih funkcij. Z notranjo predstavitvijo vtables in _vptr nam navidezne funkcije pomagajo natančno poklicati želene funkcije. V tej vadnici smo podrobno videli polimorfizem med izvajanjem, ki se uporablja v jeziku C ++.
S tem zaključujemo naše vaje o objektno usmerjenem programiranju v jeziku C ++. Upamo, da bo ta vadnica koristna za boljše in boljše razumevanje objektno usmerjenih konceptov programiranja v jeziku C ++.
=> Obiščite tukaj, če se želite naučiti C ++ iz nič.
Priporočeno branje
- Polimorfizem v jeziku C ++
- Dedovanje v jeziku C ++
- Prijateljske funkcije v C ++
- Razredi in predmeti v jeziku C ++
- Uporaba razreda selena za izbiro spustnih elementov na spletni strani - vadnica za selenij št. 13
- Vadnica za glavne funkcije Pythona s praktičnimi primeri
- Navidezni stroj Java: kako JVM pomaga pri zagonu aplikacije Java
- Kako nastaviti datoteke skripta LoadRunner VuGen in nastavitve izvajalnega okolja