Virtuali funkcija (taip pat žinoma kaip virtualūs metodai) yra nario funkcija, kuri deklaruojama bazinėje klasėje ir yra iš naujo apibrėžta (nepaisoma) išvestinės klasės. Kai nurodote išvestinės klasės objektą naudodami žymeklį arba nuorodą į bazinę klasę, galite iškviesti virtualią to objekto funkciją ir vykdyti išvestinės klasės metodo versiją.
galutinis raktinis žodis java
- Virtualios funkcijos užtikrina, kad objektui būtų iškviesta teisinga funkcija, neatsižvelgiant į funkcijos iškvietimui naudojamos nuorodos (arba rodyklės) tipą.
- Jie daugiausia naudojami vykdymo laiko polimorfizmui pasiekti.
- Funkcijos deklaruojamos a virtualus raktinis žodis bazinėje klasėje.
- Funkcijos iškvietimo sprendimas atliekamas vykdymo metu.
Virtualių funkcijų taisyklės
Virtualių funkcijų C++ taisyklės yra šios:
- Virtualios funkcijos negali būti statinės.
- Virtuali funkcija gali būti kitos klasės draugo funkcija.
- Norint pasiekti vykdymo laiko polimorfizmą, virtualiosios funkcijos turėtų būti pasiekiamos naudojant žymeklį arba bazinės klasės tipo nuorodą.
- Virtualių funkcijų prototipas turi būti toks pat tiek bazinėje, tiek išvestinėje klasėje.
- Jie visada apibrėžiami pagrindinėje klasėje ir nepaisomi išvestinėje klasėje. Išvestinei klasei neprivaloma perrašyti (arba iš naujo apibrėžti virtualiąją funkciją), tokiu atveju naudojama pagrindinės funkcijos klasės versija.
- Klasė gali turėti virtualų naikintuvą, bet negali turėti virtualaus konstruktoriaus.
Virtualių funkcijų kompiliavimo laikas (ankstyvas susiejimas) VS vykdymo laikas (vėlyvas susiejimas).
Apsvarstykite šią paprastą programą, rodančią virtualių funkcijų veikimo laiką.
C++
// C++ program to illustrate> // concept of Virtual Functions> #include> using> namespace> std;> class> base {> public>:> >virtual> void> print() { cout <<>'print base class
'>; }> >void> show() { cout <<>'show base class
'>; }> };> class> derived :>public> base {> public>:> >void> print() { cout <<>'print derived class
'>; }> >void> show() { cout <<>'show derived class
'>; }> };> int> main()> {> >base* bptr;> >derived d;> >bptr = &d;> >// Virtual function, binded at runtime> >bptr->spausdinti();> >// Non-virtual function, binded at compile time> >bptr->rodyti();> >return> 0;> }> |
>
>Išvestis
print derived class show base class>
Paaiškinimas: Vykdymo laiko polimorfizmas pasiekiamas tik naudojant bazinės klasės tipo rodyklę (arba nuorodą). Be to, bazinės klasės rodyklė gali nukreipti į bazinės klasės objektus, taip pat į išvestinės klasės objektus. Aukščiau pateiktame kode pagrindinės klasės rodyklėje „bptr“ yra išvestinės klasės objekto „d“ adresas.
Vėlyvas susiejimas (vykdymo laikas) atliekamas pagal žymeklio turinį (t. y. vietą, kurią nurodo žymeklis), o ankstyvas susiejimas (kompiliavimo laikas) atliekamas pagal žymeklio tipą, nes funkcija print() deklaruojama su virtualiu raktinis žodis, todėl jis bus susietas vykdymo metu (išvestis yra spausdinti išvestinę klasę kadangi žymeklis nurodo į išvestinės klasės objektą) ir show() yra nevirtualus, todėl kompiliavimo metu jis bus susietas (išvestis yra parodyti bazinę klasę kadangi rodyklė yra bazinio tipo).
Pastaba: Jei sukūrėme virtualią funkciją pagrindinėje klasėje ir ji yra nepaisoma išvestinėje klasėje, tada mums nereikia virtualaus raktinio žodžio išvestinėje klasėje, funkcijos automatiškai laikomos virtualiomis funkcijomis išvestinėje klasėje.
Virtualių funkcijų veikimas (VTABLE ir VPTR koncepcija)
Kaip aptarta čia, jei klasėje yra virtuali funkcija, tada pats kompiliatorius atlieka du dalykus.
- Jei sukuriamas tos klasės objektas, tada a virtualus žymeklis (VPTR) įterpiamas kaip klasės duomenų narys, nurodantis tos klasės VTABLE. Kiekvienam naujam sukurtam objektui įterpiamas naujas virtualus žymeklis kaip tos klasės duomenų narys.
- Nepriklausomai nuo to, ar objektas sukurtas, ar ne, klasėje yra narys statinis funkcijų rodyklių masyvas, vadinamas VTABLE . Šios lentelės langeliuose saugomas kiekvienos toje klasėje esančios virtualios funkcijos adresas.
Apsvarstykite toliau pateiktą pavyzdį:

C++
// C++ program to illustrate> // working of Virtual Functions> #include> using> namespace> std;> class> base {> public>:> >void> fun_1() { cout <<>'base-1
'>; }> >virtual> void> fun_2() { cout <<>'base-2
'>; }> >virtual> void> fun_3() { cout <<>'base-3
'>; }> >virtual> void> fun_4() { cout <<>'base-4
'>; }> };> class> derived :>public> base {> public>:> >void> fun_1() { cout <<>'derived-1
'>; }> >void> fun_2() { cout <<>'derived-2
'>; }> >void> fun_4(>int> x) { cout <<>'derived-4
'>; }> };> int> main()> {> >base* p;> >derived obj1;> >p = &obj1;> >// Early binding because fun1() is non-virtual> >// in base> >p->fun_1();> >// Late binding (RTP)> >p->fun_2();> >// Late binding (RTP)> >p->fun_3();> >// Late binding (RTP)> >p->fun_4();> >// Early binding but this function call is> >// illegal (produces error) because pointer> >// is of base type and function is of> >// derived class> >// p->fun_4(5);> >return> 0;> }> |
>
>Išvestis
base-1 derived-2 base-3 base-4>
Paaiškinimas: Iš pradžių sukuriame bazinės klasės tipo rodyklę ir inicijuojame ją išvestinės klasės objekto adresu. Kai sukuriame išvestinės klasės objektą, kompiliatorius sukuria žymeklį kaip klasės duomenų narį, kuriame yra išvestinės klasės VTABLE adresas.
Panaši koncepcija Vėlyvas ir ankstyvas įrišimas naudojamas kaip aukščiau pateiktame pavyzdyje. Funkcijos fun_1() iškvietimui iškviečiama pagrindinės funkcijos klasės versija, fun_2() yra nepaisoma išvestinėje klasėje, todėl vadinama išvestinė klasės versija, fun_3() nėra nepaisoma išvestinėje klasėje ir yra virtuali funkcija. todėl vadinama pagrindinės klasės versija, panašiai fun_4() nepaisoma, todėl iškviečiama pagrindinės klasės versija.
Pastaba: fun_4(int) išvestinėje klasėje skiriasi nuo virtualios funkcijos fun_4() bazinėje klasėje, nes abiejų funkcijų prototipai skiriasi.
Virtualių funkcijų apribojimai
- Lėtesnis: funkcijos iškvietimas užtrunka šiek tiek ilgiau dėl virtualaus mechanizmo ir kompiliatoriui tampa sunkiau optimizuoti, nes jis tiksliai nežino, kuri funkcija bus iškviesta kompiliavimo metu. Sunku derinti: sudėtingoje sistemoje dėl virtualių funkcijų gali būti šiek tiek sunkiau išsiaiškinti, iš kur funkcija iškviečiama.