Navidezna funkcija (znana tudi kot navidezne metode) je funkcija članica, ki je deklarirana znotraj osnovnega razreda in jo na novo definira (preglasi) izpeljan razred. Ko se sklicujete na predmet izpeljanega razreda z uporabo kazalca ali sklicevanja na osnovni razred, lahko pokličete navidezno funkcijo za ta objekt in izvedete različico metode izpeljanega razreda.
- Navidezne funkcije zagotavljajo, da je za objekt poklicana pravilna funkcija, ne glede na vrsto sklica (ali kazalca), uporabljenega za klic funkcije.
- Uporabljajo se predvsem za doseganje izvajalnega polimorfizma.
- Funkcije so deklarirane z a virtualni ključna beseda v osnovnem razredu.
- Razrešitev klica funkcije se izvede med izvajanjem.
Pravila za virtualne funkcije
Pravila za virtualne funkcije v C++ so naslednja:
- Virtualne funkcije ne morejo biti statične.
- Navidezna funkcija je lahko prijateljska funkcija drugega razreda.
- Navidezne funkcije je treba dostopati z uporabo kazalca ali reference tipa osnovnega razreda, da se doseže polimorfizem izvajalnega časa.
- Prototip virtualnih funkcij mora biti enak v osnovnem in izpeljanem razredu.
- Vedno so definirani v osnovnem razredu in preglaseni v izpeljanem razredu. Ni obvezno, da izpeljani razred preglasi (ali ponovno definira virtualno funkcijo), v tem primeru se uporabi različica osnovnega razreda funkcije.
- Razred ima lahko navidezni destruktor, ne more pa imeti navideznega konstruktorja.
Vedenje navideznih funkcij v času prevajanja (zgodnje povezovanje) VS med izvajanjem (pozno povezovanje).
Razmislite o naslednjem preprostem programu, ki prikazuje obnašanje med izvajanjem virtualnih funkcij.
C++
datum na niz
// 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->natisni();>> >// Non-virtual function, binded at compile time> >bptr->pokaži();> >return> 0;> }> |
>
>Izhod
python shrani json v datoteko
print derived class show base class>
Pojasnilo: Polimorfizem izvajalnega okolja je dosežen le prek kazalca (ali reference) tipa osnovnega razreda. Prav tako lahko kazalec osnovnega razreda kaže na objekte osnovnega razreda kot tudi na objekte izpeljanega razreda. V zgornji kodi kazalec osnovnega razreda 'bptr' vsebuje naslov predmeta 'd' izpeljanega razreda.
Pozno povezovanje (izvajalni čas) se izvede v skladu z vsebino kazalca (tj. lokacijo, na katero kaže kazalec), zgodnje povezovanje (čas prevajanja) pa glede na vrsto kazalca, saj je funkcija print() deklarirana z navideznim ključna beseda, tako da bo vezana med izvajanjem (izhod je tiskanje izpeljanega razreda ker kazalec kaže na objekt izpeljanega razreda) in show() ni navidezen, zato bo vezan med časom prevajanja (izhod je pokaži osnovni razred ker je kazalec osnovnega tipa).
Opomba: Če smo ustvarili navidezno funkcijo v osnovnem razredu in jo preglasimo v izpeljanem razredu, potem ne potrebujemo virtualne ključne besede v izpeljanem razredu, funkcije se samodejno obravnavajo kot navidezne funkcije v izpeljanem razredu.
Delovanje virtualnih funkcij (koncept VTABLE in VPTR)
Kot je razloženo tukaj, če razred vsebuje navidezno funkcijo, prevajalnik sam naredi dve stvari.
- Če je objekt tega razreda ustvarjen, potem a virtualni kazalec (VPTR) je vstavljen kot podatkovni član razreda, da kaže na VTABLE tega razreda. Za vsak nov ustvarjen objekt se kot podatkovni član tega razreda vstavi nov virtualni kazalec.
- Ne glede na to, ali je objekt ustvarjen ali ne, razred vsebuje kot člana statično polje funkcijskih kazalcev, imenovano VTABLE . Celice te tabele hranijo naslov vsake virtualne funkcije v tem razredu.
Razmislite o spodnjem primeru:

C++
1 milijon koliko 0
// 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->zabava_1();>> >// Late binding (RTP)> >p->zabava_2();>> >// Late binding (RTP)> >p->zabava_3();>> >// Late binding (RTP)> >p->zabavno_4();>> >// Early binding but this function call is> >// illegal (produces error) because pointer> >// is of base type and function is of> >// derived class> >// p->zabavno_4(5);>> >return> 0;> }> |
>
>Izhod
base-1 derived-2 base-3 base-4>
Pojasnilo: Na začetku ustvarimo kazalec osnovnega razreda tipa in ga inicializiramo z naslovom objekta izpeljanega razreda. Ko ustvarimo objekt izpeljanega razreda, prevajalnik ustvari kazalec kot podatkovni član razreda, ki vsebuje naslov VTABLE izpeljanega razreda.
Podoben koncept Pozna in zgodnja vezava se uporablja kot v zgornjem primeru. Za klic funkcije fun_1() se pokliče različica osnovnega razreda funkcije, fun_2() se preglasi v izpeljanem razredu, tako da se pokliče različica izpeljanega razreda, fun_3() se ne preglasi v izpeljanem razredu in je navidezna funkcija zato se kliče različica osnovnega razreda, podobno fun_4() ni preglasen, zato se kliče različica osnovnega razreda.
Opomba: fun_4(int) v izpeljanem razredu se razlikuje od virtualne funkcije fun_4() v osnovnem razredu, saj sta prototipa obeh funkcij različna.
Omejitve virtualnih funkcij
- Počasneje: Klic funkcije traja nekoliko dlje zaradi navideznega mehanizma in prevajalniku je težje optimizirati, ker ne ve natančno, katera funkcija bo klicana v času prevajanja. Težko odpravljanje napak: V zapletenem sistemu je lahko zaradi navideznih funkcij nekoliko težje ugotoviti, od kod se funkcija kliče.