Virtuālā funkcija (pazīstama arī kā virtuālās metodes) ir locekļa funkcija, kas tiek deklarēta pamatklasē un tiek atkārtoti definēta (ignorē) ar atvasinātu klasi. Kad atsaucaties uz atvasinātu klases objektu, izmantojot rādītāju vai atsauci uz bāzes klasi, varat izsaukt šī objekta virtuālo funkciju un izpildīt atvasinātās klases metodes versiju.
- Virtuālās funkcijas nodrošina, ka objektam tiek izsaukta pareizā funkcija neatkarīgi no funkcijas izsaukumā izmantotās atsauces (vai rādītāja) veida.
- Tos galvenokārt izmanto, lai sasniegtu izpildlaika polimorfismu.
- Funkcijas tiek deklarētas ar a virtuāls atslēgvārds pamatklasē.
- Funkcijas izsaukuma atrisināšana tiek veikta izpildlaikā.
Virtuālo funkciju noteikumi
Virtuālo funkciju noteikumi C++ ir šādi:
- Virtuālās funkcijas nevar būt statiskas.
- Virtuālā funkcija var būt citas klases drauga funkcija.
- Lai panāktu izpildlaika polimorfismu, virtuālajām funkcijām ir jāpiekļūst, izmantojot bāzes klases tipa rādītāju vai atsauci.
- Virtuālo funkciju prototipam jābūt vienādam gan bāzē, gan atvasinātajā klasē.
- Tie vienmēr ir definēti bāzes klasē un ignorēti atvasinātajā klasē. Atvasinātajai klasei nav obligāti jāpārceļ (vai jāpārdefinē virtuālā funkcija), tādā gadījumā tiek izmantota funkcijas bāzes klases versija.
- Klasei var būt virtuāls destruktors, bet tai nevar būt virtuālais konstruktors.
Virtuālo funkciju kompilēšanas laiks (agrīna saistīšana) VS izpildlaika (vēlā saistīšana).
Apsveriet šādu vienkāršo programmu, kas parāda virtuālo funkciju izpildes laiku.
C++
iebilst pret json java
// 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->print();> >// Non-virtual function, binded at compile time> >bptr->parādīt();> >return> 0;> }> |
>
>
java programmēšanas masīviIzvade
print derived class show base class>
Paskaidrojums: Izpildlaika polimorfisms tiek panākts tikai ar bāzes klases tipa rādītāju (vai atsauci). Arī bāzes klases rādītājs var norādīt uz bāzes klases objektiem, kā arī uz atvasinātās klases objektiem. Iepriekš minētajā kodā bāzes klases rādītājs “bptr” satur atvasinātās klases objekta “d” adresi.
Vēlā saistīšana (Runtime) tiek veikta atbilstoši rādītāja saturam (t.i., vietai, uz kuru norāda rādītājs), un agrīnā saistīšana (kompilēšanas laiks) tiek veikta atbilstoši rādītāja veidam, jo funkcija print() tiek deklarēta ar virtuālo funkciju. atslēgvārds, lai tas būtu saistīts izpildlaikā (izvade ir drukāt atvasināta klase jo rādītājs norāda uz atvasinātās klases objektu) un show() nav virtuāls, tāpēc kompilēšanas laikā tas tiks saistīts (izvade ir parādīt bāzes klasi jo rādītājs ir bāzes tipa).
Piezīme: Ja pamata klasē esam izveidojuši virtuālu funkciju un atvasinātajā klasē tā tiek ignorēta, tad atvasinātajā klasē nav nepieciešams virtuāls atslēgvārds, funkcijas tiek automātiski uzskatītas par virtuālajām funkcijām atvasinātajā klasē.
Virtuālo funkciju darbība (VTABLE un VPTR jēdziens)
Kā minēts šeit, ja klasē ir virtuāla funkcija, tad pats kompilators veic divas lietas.
- Ja tiek izveidots šīs klases objekts, tad a virtuālais rādītājs (VPTR) tiek ievietots kā klases datu dalībnieks, lai norādītu uz šīs klases VTABLE. Katram jaunam izveidotajam objektam kā šīs klases datu dalībnieks tiek ievietots jauns virtuālais rādītājs.
- Neatkarīgi no tā, vai objekts ir izveidots vai nē, klasē ietilpst kā dalībnieks statisks funkciju rādītāju masīvs, ko sauc par VTABLE . Šīs tabulas šūnās tiek saglabāta katras šajā klasē ietvertās virtuālās funkcijas adrese.
Apsveriet tālāk sniegto piemēru:

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;> }> |
>
atsperu karkass
>Izvade
base-1 derived-2 base-3 base-4>
Paskaidrojums: Sākotnēji mēs izveidojam tipa bāzes klases rādītāju un inicializējam to ar atvasinātā klases objekta adresi. Kad mēs izveidojam atvasinātās klases objektu, kompilators izveido rādītāju kā klases datu dalībnieku, kas satur atvasinātās klases VTABLE adresi.
Līdzīga koncepcija Vēlīnā un agrīnā iesiešana tiek izmantots tāpat kā iepriekš minētajā piemērā. Fun_1() funkcijas izsaukšanai tiek izsaukta funkcijas bāzes klases versija, atvasinātajā klasē fun_2() tiek ignorēta, tāpēc tiek izsaukta atvasinātā klases versija, fun_3() netiek ignorēta atvasinātajā klasē un ir virtuāla funkcija. tāpēc tiek izsaukta bāzes klases versija, tāpat fun_4() netiek ignorēta, tāpēc tiek izsaukta bāzes klases versija.
Piezīme: fun_4(int) atvasinātajā klasē atšķiras no virtuālās funkcijas fun_4() pamata klasē, jo abu funkciju prototipi ir atšķirīgi.
Virtuālo funkciju ierobežojumi
- Lēnāk: funkcijas izsaukšana aizņem nedaudz ilgāku laiku virtuālā mehānisma dēļ un apgrūtina kompilatora optimizāciju, jo tas precīzi nezina, kura funkcija tiks izsaukta kompilēšanas laikā. Grūti atkļūdot: Sarežģītā sistēmā virtuālās funkcijas var nedaudz apgrūtināt izdomāt, no kurienes funkcija tiek izsaukta.