En virtuell funksjon (også kjent som virtuelle metoder) er en medlemsfunksjon som er deklarert innenfor en basisklasse og redefinert (overstyrt) av en avledet klasse. Når du refererer til et avledet klasseobjekt ved å bruke en peker eller en referanse til basisklassen, kan du kalle en virtuell funksjon for det objektet og kjøre den avledede klassens versjon av metoden.
eksempel binært søketre
- Virtuelle funksjoner sikrer at riktig funksjon kalles for et objekt, uavhengig av type referanse (eller peker) som brukes for funksjonskallet.
- De brukes hovedsakelig for å oppnå Runtime polymorfisme.
- Funksjoner er deklarert med en virtuell nøkkelord i en basisklasse.
- Løsningen av et funksjonskall gjøres ved kjøretid.
Regler for virtuelle funksjoner
Reglene for de virtuelle funksjonene i C++ er som følger:
- Virtuelle funksjoner kan ikke være statiske.
- En virtuell funksjon kan være en vennefunksjon av en annen klasse.
- Virtuelle funksjoner bør nås ved å bruke en peker eller referanse av basisklassetype for å oppnå runtime polymorfisme.
- Prototypen av virtuelle funksjoner bør være den samme i basen så vel som den avledede klassen.
- De er alltid definert i basisklassen og overstyrt i en avledet klasse. Det er ikke obligatorisk for den avledede klassen å overstyre (eller omdefinere den virtuelle funksjonen), i så fall brukes basisklasseversjonen av funksjonen.
- En klasse kan ha en virtuell destruktor, men den kan ikke ha en virtuell konstruktør.
Kompilere tid (tidlig binding) VS kjøretid (sen binding) oppførsel av virtuelle funksjoner
Tenk på følgende enkle program som viser kjøretidsoppførselen til virtuelle funksjoner.
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->print();> >// Non-virtual function, binded at compile time> >bptr->vis();> >return> 0;> }> |
>
>Produksjon
print derived class show base class>
Forklaring: Runtime polymorfisme oppnås bare gjennom en peker (eller referanse) av basisklassetypen. En grunnklassepeker kan også peke til objektene til basisklassen så vel som objektene til den avledede klassen. I koden ovenfor inneholder grunnklassepekeren 'bptr' adressen til objektet 'd' til den avledede klassen.
Sen binding (Runtime) gjøres i samsvar med innholdet i pekeren (dvs. plassering pekt på av pekeren) og tidlig binding (Compile-time) gjøres i henhold til pekertypen siden print()-funksjonen er deklarert med den virtuelle nøkkelordet slik at det vil være bundet ved kjøretid (utdata er trykkavledet klasse som pekeren peker på objektet av avledet klasse) og show() er ikke-virtuell, så den vil være bundet under kompileringstiden (utdata er vis grunnklasse siden pekeren er av basetype).
Merk: Hvis vi har opprettet en virtuell funksjon i basisklassen og den blir overstyrt i den avledede klassen, trenger vi ikke et virtuelt nøkkelord i den avledede klassen, funksjoner betraktes automatisk som virtuelle funksjoner i den avledede klassen.
Bruk av virtuelle funksjoner (konseptet VTABLE og VPTR)
Som diskutert her, hvis en klasse inneholder en virtuell funksjon, gjør kompilatoren selv to ting.
- Hvis et objekt av den klassen er opprettet, kan en virtuell peker (VPTR) settes inn som et datamedlem i klassen for å peke på VTABELLEN for den klassen. For hvert nytt objekt som opprettes, settes en ny virtuell peker inn som et datamedlem i den klassen.
- Uavhengig av om objektet er opprettet eller ikke, inneholder klassen som et medlem en statisk rekke funksjonspekere kalt VTABLE . Celler i denne tabellen lagrer adressen til hver virtuelle funksjon i den klassen.
Tenk på eksempelet nedenfor:

C++
java-inndatastreng
// 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;> }> |
>
>Produksjon
base-1 derived-2 base-3 base-4>
Forklaring: Til å begynne med lager vi en peker av typen basisklasse og initialiserer den med adressen til det avledede klasseobjektet. Når vi lager et objekt av den avledede klassen, lager kompilatoren en peker som et datamedlem av klassen som inneholder adressen til VTABLE til den avledede klassen.
Et lignende konsept av Sen og tidlig innbinding brukes som i eksemplet ovenfor. For fun_1() funksjonskallet kalles basisklasseversjonen av funksjonen, fun_2() overstyres i den avledede klassen slik at den avledede klasseversjonen kalles, fun_3() overstyres ikke i den avledede klassen og er en virtuell funksjon så grunnklasseversjonen kalles, på samme måte blir fun_4() ikke overstyrt så grunnklasseversjonen kalles.
Merk: fun_4(int) i den avledede klassen er forskjellig fra den virtuelle funksjonen fun_4() i basisklassen ettersom prototyper av begge funksjonene er forskjellige.
Begrensninger for virtuelle funksjoner
- Langsommere: Funksjonskallet tar litt lengre tid på grunn av den virtuelle mekanismen og gjør det vanskeligere for kompilatoren å optimalisere fordi den ikke vet nøyaktig hvilken funksjon som skal kalles på kompileringstidspunktet. Vanskelig å feilsøke: I et komplekst system kan virtuelle funksjoner gjøre det litt vanskeligere å finne ut hvor en funksjon kalles fra.