logo

Virtuell funktion i C++

En virtuell funktion (även känd som virtuella metoder) är en medlemsfunktion som deklareras inom en basklass och omdefinieras (åtsidosätts) av en härledd klass. När du refererar till ett härlett klassobjekt med hjälp av en pekare eller en referens till basklassen, kan du anropa en virtuell funktion för det objektet och köra den härledda klassens version av metoden.

  • Virtuella funktioner säkerställer att rätt funktion anropas för ett objekt, oavsett vilken typ av referens (eller pekare) som används för funktionsanropet.
  • De används främst för att uppnå Runtime-polymorfism.
  • Funktioner deklareras med a virtuell nyckelord i en basklass.
  • Lösningen av ett funktionsanrop görs vid körning.

Regler för virtuella funktioner

Reglerna för de virtuella funktionerna i C++ är följande:

  1. Virtuella funktioner kan inte vara statiska.
  2. En virtuell funktion kan vara en vänfunktion i en annan klass.
  3. Virtuella funktioner bör nås med en pekare eller referens av basklasstyp för att uppnå körtidspolymorfism.
  4. Prototypen av virtuella funktioner bör vara densamma i basen såväl som den härledda klassen.
  5. De definieras alltid i basklassen och åsidosätts i en härledd klass. Det är inte obligatoriskt för den härledda klassen att åsidosätta (eller omdefiniera den virtuella funktionen), i så fall används basklassversionen av funktionen.
  6. En klass kan ha en virtuell destruktor men den kan inte ha en virtuell konstruktor.

Kompileringstid (tidig bindning) VS körtid (sen bindning) beteende för virtuella funktioner

Tänk på följande enkla program som visar körtidsbeteendet för virtuella funktioner.



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->visa();> >return> 0;> }>

>

>

Produktion

print derived class show base class>

Förklaring: Runtime polymorfism uppnås endast genom en pekare (eller referens) av basklasstypen. En basklasspekare kan också peka på objekten i basklassen såväl som till objekten i den härledda klassen. I ovanstående kod innehåller basklasspekaren 'bptr' adressen till objektet 'd' för den härledda klassen.

Sen bindning (Runtime) görs i enlighet med innehållet i pekaren (dvs. plats som pekas på av pekaren) och tidig bindning (Compile-time) görs enligt typen av pekare eftersom print()-funktionen deklareras med den virtuella nyckelordet så att det kommer att bindas vid körning (utgången är tryckt härledd klass eftersom pekaren pekar på objekt av härledd klass) och show() är icke-virtuell så den kommer att bindas under kompileringstiden (utgången är visa basklass eftersom pekaren är av bastyp).

Notera: Om vi ​​har skapat en virtuell funktion i basklassen och den åsidosätts i den härledda klassen behöver vi inte ett virtuellt nyckelord i den härledda klassen, funktioner betraktas automatiskt som virtuella funktioner i den härledda klassen.

Att arbeta med virtuella funktioner (konceptet VTABLE och VPTR)

Som diskuterats här, om en klass innehåller en virtuell funktion gör kompilatorn själv två saker.

  1. Om ett objekt av den klassen skapas, kommer a virtuell pekare (VPTR) infogas som en datamedlem i klassen för att peka på VTABELL för den klassen. För varje nytt objekt som skapas, infogas en ny virtuell pekare som en datamedlem i den klassen.
  2. Oavsett om objektet är skapat eller inte, innehåller klassen som en medlem en statisk array av funktionspekare som kallas VTABLE . Celler i denna tabell lagrar adressen för varje virtuell funktion som finns i den klassen.

Tänk på exemplet nedan:

virtuell pekare och virtuell tabell

C++




kamelfodral python
// 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;> }>

>

>

Produktion

base-1 derived-2 base-3 base-4>

Förklaring: Inledningsvis skapar vi en pekare av typen basklass och initierar den med adressen till det härledda klassobjektet. När vi skapar ett objekt av den härledda klassen, skapar kompilatorn en pekare som en datamedlem i klassen som innehåller adressen till VTABLE för den härledda klassen.

Ett liknande koncept för Sen och tidig bindning används som i exemplet ovan. För funktionsanropet fun_1() anropas basklassversionen av funktionen, fun_2() åsidosätts i den härledda klassen så att den härledda klassversionen anropas, fun_3() åsidosätts inte i den härledda klassen och är en virtuell funktion så basklassversionen anropas, på samma sätt åsidosätts inte fun_4() så basklassversionen anropas.

Notera: fun_4(int) i den härledda klassen skiljer sig från den virtuella funktionen fun_4() i basklassen eftersom prototyper för båda funktionerna är olika.

Begränsningar för virtuella funktioner

    Långsammare: Funktionsanropet tar något längre tid på grund av den virtuella mekanismen och gör det svårare för kompilatorn att optimera eftersom den inte vet exakt vilken funktion som kommer att anropas vid kompileringstillfället. Svårt att felsöka: I ett komplext system kan virtuella funktioner göra det lite svårare att ta reda på var en funktion anropas från.