Virtuaalitoiminto - Virtual function
| Polymorfismi |
|---|
| Ad hoc -polymorfismi |
| Parametrinen polymorfismi |
| Alalaji |
In olio-ohjelmointi , kielillä kuten C ++ , ja Object Pascal , joka on virtuaalinen funktio tai virtuaalinen menetelmä on periytyvä ja kumoavat toiminta tai menetelmä varten, joka myöhäinen sidonta helpottuu. Tämä käsite on tärkeä osa (runtime) polymorfismi osan olio-ohjelmointi (OOP). Lyhyesti sanottuna virtuaalifunktio määrittelee suoritettavan kohdefunktion, mutta kohde ei välttämättä ole tiedossa käännösaikana.
Useimmat ohjelmointikielet, kuten Java , PHP ja Python , pitävät kaikkia menetelmiä oletuksena virtuaalisina eivätkä tarjoa muokkaajaa tämän toiminnan muuttamiseksi. Jotkut kielet tarjoavat kuitenkin muokkaimia estääkseen menetelmiä ohittamasta johdettuja luokkia (kuten Javan ja PHP: n lopullinen avainsana ).
Tarkoitus
Virtuaalitoiminnon käsite ratkaisee seuraavan ongelman:
Kun olio-ohjelmoinnissa johdettu luokka periytyy perusluokasta, johdetun luokan objektiin voidaan viitata perusluokan tyypin osoittimen tai viitteen kautta johdetun luokkatyypin sijaan. Jos johdettu luokka ohittaa perusluokan menetelmiä, tällaisen viitteen tai osoittimen kutsuma menetelmä voidaan sitoa (linkittää) joko 'varhain' (kääntäjä) osoittimen tai viitteen ilmoitetun tyypin mukaan, tai 'myöhään' (eli kielen ajonaikaisen järjestelmän mukaan), sen kohteen todellisen tyypin mukaan, johon viitataan.
Virtuaalitoiminnot ratkaistaan "myöhään". Jos kyseinen funktio on perusluokassa 'virtuaalinen', funktion eniten johdettua toteutusta kutsutaan viitatun objektin todellisen tyypin mukaan riippumatta osoittimen tai viitteen ilmoitetusta tyypistä. Jos se ei ole "virtuaalinen", menetelmä ratkaistaan "aikaisin" ja valitaan osoittimen tai viitteen ilmoitetun tyypin mukaan.
Virtuaalitoimintojen avulla ohjelma voi kutsua menetelmiä, joita ei välttämättä edes ole olemassa koodin kokoamishetkellä.
C ++: ssa virtuaaliset menetelmät ilmoitetaan lisäämällä avainsana funktion ilmoitukseen perusluokassa . Tämän muokkaajan perivät kaikki kyseisen menetelmän toteutukset johdetuissa luokissa, mikä tarkoittaa, että ne voivat edelleen ylittää toisiaan ja olla myöhässä. Ja vaikka perusluokan omistamat menetelmät kutsuvat virtuaalimenetelmää, he kutsuvat sen sijaan johdettua menetelmää. Ylikuormitus tapahtuu, kun saman luokan kahdella tai useammalla menetelmällä on sama metodinimi, mutta eri parametrit. Ohittaminen tarkoittaa kahta menetelmää, joilla on sama metodinimi ja parametrit. Ylikuormitusta kutsutaan myös toimintojen sovittamiseksi ja ohitusta dynaamiseksi toimintojen kartoittamiseksi.
virtual
Esimerkki
Esimerkiksi perusluokalla Animalvoi olla virtuaalitoiminto Eat. Alaluokka Llamatoteuttaisi Eateri tavalla kuin alaluokka Wolf, mutta voidaan vedota Eatmihin tahansa luokkaan, jota kutsutaan eläimeksi, ja saada Eattietyn alaluokan käyttäytyminen.
class Animal {
public:
// Intentionally not virtual:
void Move(void) {
std::cout << "This animal moves in some way" << std::endl;
}
virtual void Eat(void) = 0;
};
// The class "Animal" may possess a definition for Eat if desired.
class Llama : public Animal {
public:
// The non virtual function Move is inherited but not overridden.
void Eat(void) override {
std::cout << "Llamas eat grass!" << std::endl;
}
};
Tämän avulla ohjelmoija voi käsitellä luetteloa luokan esineistä ja Animalkehottaa jokaista syömään (soittamalla Eat) tarvitsematta tietää, millainen eläin voi olla luettelossa, miten jokainen eläin syö tai mitä kaikkia mahdollisia eläinlajeja voi olla.
Voimme nähdä paremmin, miten virtuaalitoiminnot toimivat toteuttamalla yllä oleva esimerkki C: ssä
#include <stdio.h>
/* an object points to its class... */
struct Animal {
const struct AnimalClass * class;
};
/* which contains the virtual function Animal.Eat */
struct AnimalClass {
void (*Eat)(struct Animal *); // 'virtual' function
};
/* Since Animal.Move is not a virtual function
it is not in the structure above. */
void Move(struct Animal * self)
{
printf("<Animal at %p> moved in some way\n", (void *) self);
}
/* unlike Move, which executes Animal.Move directly,
Eat cannot know which function (if any) to call at compile time.
Animal.Eat can only be resolved at run time when Eat is called. */
void Eat(struct Animal * self)
{
const struct AnimalClass * class = *(const void **) self;
if (class->Eat)
class->Eat(self); // execute Animal.Eat
else
fprintf(stderr, "Eat not implemented\n");
}
/* implementation of Llama.Eat this is the target function
to be called by 'void Eat(struct Animal *).' */
static void _Llama_eat(struct Animal * self)
{
printf("<Llama at %p> Llama's eat grass!\n", (void *) self);
}
/* initialize class */
const struct AnimalClass Animal = {(void *) 0}; // base class does not implement Animal.Eat
const struct AnimalClass Llama = {_Llama_eat}; // but the derived class does
int main(void)
{
/* init objects as instance of its class */
struct Animal animal = {& Animal};
struct Animal llama = {& Llama};
Move(& animal); // Animal.Move
Move(& llama); // Llama.Move
Eat(& animal); // cannot resolve Animal.Eat so print "Not Implemented" to stderr
Eat(& llama); // resolves Llama.Eat and executes
}
Abstraktit luokat ja puhtaat virtuaalitoiminnot
Puhdas virtuaalinen funktio tai puhdasta virtuaalista menetelmä on virtuaalinen funktio, joka tarvitaan toteuttaa johdetun luokan jos johdettuja luokka ei ole abstrakti . Luokkia, jotka sisältävät puhtaita virtuaalisia menetelmiä, kutsutaan "abstrakteiksi", eikä niitä voida ilmaista suoraan. Alaluokka , joka abstrakti luokka voidaan instantioida suoraan, jos kaikki peritty puhdasta virtuaalista menetelmiä on toteutettu tämän luokan tai vanhemman luokan. Puhtailla virtuaalisilla menetelmillä on tyypillisesti ilmoitus ( allekirjoitus ) eikä määritelmää ( toteutus ).
Esimerkiksi abstrakti perusluokka MathSymbolvoi tarjota puhtaan virtuaalitoiminnon doOperation()ja johdetut luokat Plusja Minustoteutuksen doOperation()konkreettisten toteutusten aikaansaamiseksi. Toteutus doOperation()ei olisi järkevää MathSymbolluokassa, samoin kuin MathSymbolabstrakti käsite, jonka käyttäytyminen on määritelty yksinomaan kunkin tietyn lajin (alaluokan) osalta MathSymbol. Samoin tietty alaluokka MathSymbolei olisi täydellinen ilman toteutusta
doOperation().
Vaikka puhtailla virtuaalisilla menetelmillä ei yleensä ole toteutusta niitä ilmoittavassa luokassa, joidenkin kielten (esim. C ++ ja Python) puhtaat virtuaalimenetelmät voivat sisällyttää toteutuksen niiden ilmoitusluokkaan tarjoamalla vara- tai oletuskäyttäytymisen, jolle johdettu luokka voi delegoida , jos soveliasta.
Puhtaita virtuaalifunktioita voidaan käyttää myös silloin, kun menetelmäraportteja käytetään rajapinnan määrittämiseen - samalla tavalla kuin Java -käyttöliittymän avainsana. Tällaisessa käytössä johdetut luokat toimittavat kaikki toteutukset. Tällaisessa suunnittelumallissa rajapintana toimiva abstrakti luokka sisältää vain puhtaita virtuaalitoimintoja, mutta ei datajäseniä tai tavallisia menetelmiä. C ++: ssa tällaisten puhtaasti abstraktien luokkien käyttäminen rajapintoina toimii, koska C ++ tukee moninkertaista perintöä . Koska monet OOP -kielet eivät kuitenkaan tue useaa perintöä, ne tarjoavat usein erillisen käyttöliittymämekanismin. Esimerkki on Java -ohjelmointikieli .
Käyttäytyminen rakentamisen ja tuhoamisen aikana
Kielet eroavat käyttäytymisestään objektin konstruktorin tai tuhoajan ollessa käynnissä. Tästä syystä virtuaalifunktioiden kutsumista konstruktoreissa ei yleensä suositella.
C ++: ssa "base" -funktiota kutsutaan. Tarkemmin sanottuna kutsutaan eniten johdettua funktiota, joka ei ole johdettu enemmän kuin nykyinen konstruktorin luokka. Jos tämä toiminto on puhdas virtuaalifunktio, esiintyy määrittelemätöntä käyttäytymistä . Tämä pätee, vaikka luokka sisältää toteutuksen kyseiselle puhtaalle virtuaalitoiminnolle. Mukautuvaa C ++ -käyttöä ei vaadita (eikä se yleensä pysty) havaitsemaan epäsuoria puheluita puhtaille virtuaalitoiminnoille käännös- tai linkkausaikana . Jotkut ajonaikaiset järjestelmät antavat puhtaan virtuaalifunktion kutsuvirheen, kun he kohtaavat puhelun puhtaalle virtuaalitoiminnolle ajon aikana .
Javassa ja C#: ssa johdettua toteutusta kutsutaan, mutta joissakin kentissä ei ole vielä alustettu johdettua konstruktoria (vaikka ne on alustettu oletusarvoihinsa). Jotkut suunnittelumallit , kuten Abstract Factory Pattern , edistävät aktiivisesti tätä käyttöä kielillä, jotka tukevat tätä kykyä.
Virtuaaliset tuhoajat
Objektikeskeiset kielet hallitsevat tyypillisesti muistin varausta ja varauksen poistamista automaattisesti, kun objektit luodaan ja tuhotaan. Jotkin olio-suuntautuneet kielet sallivat kuitenkin halutessaan mukautetun tuhoajamenetelmän. Jos kyseinen kieli käyttää automaattista muistinhallintaa, kutsuttu mukautettu tuhoaja (jota tässä yhteydessä kutsutaan yleisesti viimeistelyaineeksi) on varmasti sopiva kyseiselle objektille. Jos esimerkiksi luodaan Wolf -tyyppinen objekti, joka perii Animalin, ja molemmilla on mukautettuja tuhoajia, kutsutaan Wolfissa ilmoitetuksi.
Manuaalisessa muistinhallinnassa tilanne voi olla monimutkaisempi erityisesti staattisen lähetyksen suhteen . Jos Wolf -tyyppinen objekti luodaan, mutta se osoittaa eläimen osoitin, ja tämä eläimen osoitintyyppi poistetaan, kutsuttu tuhoaja voi itse asiassa olla eläimelle määritetty eikä Wolfille, ellei tuhoaja ole virtuaalinen . Tämä koskee erityisesti C ++: ta, jossa toiminta on yleinen ohjelmointivirheiden lähde, jos tuhoajat eivät ole virtuaalisia.
Katso myös
- Abstrakti menetelmä
- Perintö
- Superclass
- Virtuaalinen perintö
- Liitäntä (olio -ohjelmointi)
- Komponenttikohdemalli
- Virtuaalimenetelmätaulukko