Ce este legarea timpurie și tardivă? Diferența dintre metodele de anulare și ascunderea. Designeri. Cuvinte rezervate super și asta. Blocuri de inițializare

10.11.2019 Programe

Pentru a afla care este diferența între timpuriu (static) Și tarziu (dinamic) obligatoriu în Java, mai întâi trebuie să înțelegeți ce este legând . Legarea înseamnă că există o legătură între legătură și cod. De exemplu, o variabilă la care faceți referire este legată de codul în care este definită. De asemenea, metoda apelată este legată de locația din cod în care este definită.

Există două tipuri de legare a metodelor în limbajul Java: legarea timpurie (numită și static) și legarea tardivă (respectiv, dinamică) legând . Apelarea unei metode în Java înseamnă că metoda este legată de un anumit cod, fie în timpul compilării, fie în timpul rulării, când programul rulează și obiectele sunt create. După cum sugerează și numele, legătura statică este de natură mai statică, deoarece are loc în timpul compilării, ceea ce înseamnă că codul „știe” ce metodă să apeleze după compilare cod sursaîn Java în fișiere de clasă. Și din moment ce acesta este într-un stadiu incipient ciclu de viață programul se mai numește și legarea timpurie. Pe de altă parte, legătura dinamică are loc în timpul execuției, după ce programul este rulat de mașina virtuală Java. În acest caz, metoda de apelat este determinată de obiectul specific, astfel încât informațiile nu sunt disponibile în timpul compilării, deoarece obiectele sunt create în timpul rulării. Și, deoarece acest lucru se întâmplă târziu în ciclul de viață al programului, se numește legare târzie în Java. Să ne uităm la câteva diferențe pentru a înțelege mai bine acest lucru și, de asemenea, pentru a putea răspunde la această întrebare foarte populară adresată în interviurile Java.

Legarea timpurie și târzie în Java

Există multe diferențe între legarea statică și dinamică în Java, dar cel mai important este modul în care le folosește JVM. V-ați întrebat vreodată cum decide JVM ce metodă să apeleze atunci când există mai multe metode cu același nume în domeniu? Dacă ați folosit vreodată supraîncărcarea sau suprascrierea metodelor, știți că în Java puteți avea mai multe metode cu același nume. În cazul Java mașină virtuală JVM folosește atât legarea statică, cât și dinamică pentru a selecta metoda dorită.

Exemplu de legare statică și dinamică în Java

În acest program, veți vedea că legarea metodelor virtuale nu are loc în timpul compilării folosind legarea statică, deoarece aceasta ar apela o metodă din superclasă, așa cum se întâmplă cu metodele statice care sunt legate devreme. Dacă este apelată o metodă dintr-o subclasă, atunci un obiect specific a fost folosit pentru a lega funcția în timpul execuției și, prin urmare, pentru a lega funcții virtuale este utilizată legătura dinamică. public class Main ( public static void main (Argumente șir) ( // Exemplu de legare statică și dinamică în Java Insurance current = new CarInsurance () ; // Legare dinamică bazată pe obiecte int premium = curent. premium(); // Legare statică bazată pe clasă Categoria șirurilor = curent. categorie(); Sistem. afară. println("premium:" + premium); Sistem. afară. println("categorie: " + categorie); ) ) clasa Asigurare ( public static final int LOW = 100 ; public int premium () ( return LOW; ) public static String categorie () ( return „Asigurare” ; ) ) clasa CarInsurance extinde Asigurare ( public static final int HIGH = 200 ; public int premium () ( return HIGH; ) public static String categorie () ( return "Asigurare auto" ; ) ) Rezultate execuție: premium : 200 categorie : Asigurare După cum puteți vedea, apelarea metodei premium() a dus la executarea o metodă din subclasă, în timp ce apelarea metodei categorie() a dus la executarea metodei superclasei. Acest lucru se datorează faptului că premium() este o metodă virtuală care este rezolvată folosind legarea tardivă, în timp ce categorie() este metoda statica, care este rezolvată utilizând legături statice în timp de compilare după numele clasei.
Vă interesează să citiți despre Java? Alătură-te grupului!

Diferențele dintre legarea timpurie și cea târzie în Java

Acum că aveți o înțelegere a modului în care Java leagă apelurile de metodă și a modului în care funcționează legarea statică și dinamică, să recapitulăm diferențele cheie dintre legarea timpurie și cea târzie în Java:
  1. Legătura statică are loc în timpul compilării, în timp ce legarea dinamică are loc în timpul rulării.

  2. Deoarece legarea statică are loc la începutul ciclului de viață al unui program, se numește legare timpurie. În mod similar, legarea dinamică este numită și legarea tardivă, deoarece apare mai târziu în execuția programului.

  3. Legarea statică este folosită în limbajul Java pentru a rezolva metodele supraîncărcate, în timp ce legarea dinamică este folosită în limbajul Java pentru a rezolva metodele supraîncărcate.

  4. De asemenea, metodele private, statice și terminale sunt rezolvate folosind legarea statică, deoarece nu pot fi suprascrise, în timp ce toate metodele virtuale sunt rezolvate folosind legarea dinamică.

  5. În cazul legării statice, nu obiectele concrete sunt folosite, ci informațiile de tip, adică tipul variabilei de referință este folosită pentru a descoperi metoda dorită. Pe de altă parte, legarea dinamică folosește un obiect specific pentru a găsi metoda dorită în Java.
Iată un exercițiu bun bazat pe conceptele de legare statică și dinamică în Java. Poti sa raspunzi la intrebare: „Ce va fi rezultat când următorul program este executat?” Ce va scoate acest program? Colecție, Set sau HashSet? Atât am vrut să vă spunem despre diferențele dintre timpuriu (static) Și tarziu (dinamic) legarea în Java. Acesta este unul dintre cele mai bune intrebari pentru un interviu telefonic despre limbajul Java, deoarece oferă multe oportunități de a testa cunoștințele profunde ale candidatului. Amintește-ți mereu asta privat , static Și metode finale comunica folosind legarea statică , A virtual – dinamic . De asemenea, cel mai bun exemplu de legare statică este supraîncărcarea metodei, în timp ce suprascrierea este dinamică.

2

Să spunem că nu a existat nicio funcție Bună, și doar numim ob.display practic, apoi apelează funcția de afișare din clasa B, nu clasa A.

Apelul la display() este setat o dată de compilator la versiunea definită în clasa de bază. Aceasta se numește rezoluție statică a apelurilor de funcție sau legarea statică - apelul de funcție este efectuat înainte ca programul să fie executat. Aceasta este uneori numită și legarea timpurie, deoarece funcția display() este specificată la momentul compilării programului.

Acum, cum poate apela funcția de afișare a clasei derivate fără a utiliza cuvântul cheie virtual (legare târzie) înainte de funcția de afișare în clasa de bază?

Acum, în acest program, trecerea obiectului ca apel după valoare, apel cu pointer și apel prin referire la funcția Hello funcționează bine. Acum, dacă folosim polimorfism și dorim să redăm o funcție membru a unei clase derivate dacă este numită, trebuie să adăugăm cuvânt cheie virtual înainte de funcția de mapare a bazei de date. Dacă treceți valoarea unui obiect atunci când apelați folosind un pointer și apelați prin referință, este un apel de funcție în clasa derivată, dar dacă treceți obiectul după valoare, nu de ce este așa?>

Clasa A (public: void display(); // virtual void display() (cout<< "Hey from A" <display() ) int main() ( B obj; Bună (obj); // obj //&ob return 0; )

  • 2 raspunsuri
  • Triere:

    Activitate

4

acum cum poate apela funcția de afișare a clasei derivate fără a utiliza cuvântul cheie virtual (legare târzie) înainte de funcția de afișare în clasa de bază?

O funcție non-virtuală este pur și simplu rezolvată de compilator în funcție de tipul static al obiectului (sau referință sau pointer) pe care îl apelează. Astfel, un obiect dat de tip derivat, precum și o referință la subobiectul său:

Bb; A&a=b;

veți obține rezultate diferite de la apelarea unei funcții non-virtuale:

B.afisare(); // numit ca B a.display(); // numit ca A

Dacă cunoașteți tipul real, atunci puteți specifica cum doriți să numiți această versiune:

Static_cast (un ecran(); // numit ca B

dar ceea ce ar fi teribil de greșit este dacă obiectul la care se referă a nu are tipul B .

Acum, dacă folosim Polymorphism și dorim să mapam o funcție membru a unei clase derivate dacă este apelată, trebuie să adăugăm un cuvânt cheie virtual înainte de funcția map din bază.

A corecta. Dacă faceți o funcție virtuală, aceasta va fi rezolvată în timpul execuției în funcție de tipul dinamic al obiectului, chiar dacă utilizați un alt tip de referință sau pointer pentru a-l accesa. Deci ambele exemple de mai sus l-ar numi B.

Dacă trecem valoarea unui obiect prin apel prin pointer și apelăm prin referință, apelează funcția din clasa derivată, dar dacă trecem obiectul după valoare, asta nu înseamnă de ce este așa?

Dacă îl treci după valoare, atunci tu felierea it: copierea doar a părții A a unui obiect pentru a face un nou obiect de tip A . Deci, indiferent dacă această funcție este virtuală, apelând-o pe acest obiect va selecta versiunea lui A , deoarece este A și nimic altceva decât A .

0

wikipedia spune că tăierea obiectelor are loc deoarece nu există spațiu pentru a stoca membri suplimentari ai clasei derivate în superclasă, deci este tăiată. De ce nu are loc tăierea obiectelor dacă o trecem prin referință sau indicator? De ce superclasa primește spațiu suplimentar pentru a o depozita? -

FUNCȚII VIRTUALE________________________________________________________________ 1

Legare timpurie și târzie. Polimorfismul dinamic _________________________________ 1

Funcții virtuale________________________________________________________________________ 1 Destructori virtuali ________________________________________________________________ 4 Clase abstracte și funcții virtuale pure___________________________________________ 5

FUNCȚII VIRTUALE

Legare timpurie și târzie. Polimorfism dinamic

C++ acceptă polimorfismul în două moduri.

În primul rând, este acceptat în timpul compilării prin supraîncărcarea funcțiilor și a operatorului. Acest tip de polimorfism se numește polimorfism static, deoarece este implementat chiar înainte de execuție

program, de asocierea timpurie a identificatorilor de funcții cu adresele fizice în stadiul de compilare și legare.

În al doilea rând, este suportat în timpul execuției programului prin funcții virtuale. După ce a întâlnit un apel de funcție virtuală în codul programului, compilatorul (sau, mai precis, linkerul) desemnează doar acest apel, lăsând până la etapa de execuție asocierea identificatorului funcției cu adresa sa. Acest proces se numește legarea tardivă.

O funcție virtuală este o funcție al cărei apel (și acțiunile pe care le efectuează) depinde de tipul de obiect pe care este apelată. Obiectul determină ce funcție trebuie apelată în timpul execuției programului. Acest tip de polimorfism se numește polimorfism dinamic.

bază polimorfism dinamic este capacitatea oferită de C++ de a defini un pointer către o clasă de bază, care va indica de fapt nu numai un obiect din această clasă, ci și orice obiect al unei clase derivate. Această capacitate vine prin moștenire deoarece un obiect al unei clase derivate este întotdeauna un obiect al clasei de bază. La momentul compilării, nu se știe încă ce obiect de clasă va dori să creeze utilizatorul, având în vedere un pointer către obiectul clasei de bază. Un astfel de pointer este asociat cu obiectul său numai în timpul execuției programului, adică dinamic. O clasă care conține cel puțin o funcție virtuală se numește polimorfă.

Pentru fiecare tip de date polimorfe, compilatorul creează un tabel de funcții virtuale și încorporează un pointer ascuns către acest tabel în fiecare obiect al acestei clase. Conține adresele funcțiilor virtuale ale obiectului corespunzător. Numele pointerului către tabelul de funcții virtuale și numele tabelului depind de implementarea într-un anumit compilator. De exemplu, în Visual C++ 6.0, acest indicator este numit vfptr , iar tabelul se numește vftable (din tabelul de funcții virtuale în engleză). Compilatorul încorporează automat o bucată de cod la începutul constructorului de clasă polimorfă care inițializează un pointer către tabelul de funcții virtuale. Dacă este apelată o funcție virtuală, codul generat de compilator găsește un pointer către tabelul de funcții virtuale, apoi caută tabelul respectiv și preia adresa funcției corespunzătoare din acesta. După aceasta, se face o tranziție la adresa specificată și funcția este apelată.

Amintiți-vă că atunci când creați un obiect al unei clase derivate, constructorul clasei sale de bază este mai întâi apelat. În această etapă, este creat un tabel cu funcții virtuale, precum și un pointer către acesta. După apelarea constructorului clasei derivate, indicatorul tabelului de funcții virtuale este configurat astfel încât să se refere la funcția virtuală suprascrisă (dacă există) care există pentru un obiect din acea clasă.

În acest sens, trebuie să fiți conștient de prețul pe care trebuie să-l plătiți pentru posibilitatea de a utiliza legarea tardivă.

Deoarece obiectele cu funcții virtuale trebuie să suporte și un tabel de funcții virtuale, utilizarea lor duce întotdeauna la o ușoară creștere a costurilor de memorie și la o scădere a performanței programului. Dacă lucrați cu o clasă mică pe care nu intenționați să o utilizați ca clasă de bază pentru alte clase, atunci nu are rost să folosiți funcții virtuale.

Funcții virtuale

Funcțiile a căror interfață de apelare (adică prototipul) este cunoscută, dar implementarea nu poate fi specificată în general, ci poate fi definită doar pentru cazuri specifice, se numesc virtuale (un termen care înseamnă că funcția poate fi suprascrisă într-o clasă derivată) .

Funcțiile virtuale sunt funcții care asigură apelarea funcției corecte a unui obiect, indiferent de expresia folosită pentru a efectua apelul.

Să presupunem că o clasă de bază conține o funcție care este declarată virtuală, iar o clasă derivată definește aceeași funcție. În acest caz, o funcție dintr-o clasă derivată este apelată pe obiectele clasei derivate, chiar dacă este apelată folosind un pointer sau referință la clasa de bază. Exemplu:

clasa Coord

Clasa de coordonate de bază

// clasa de coordonate de bază

protejat:

// membrii clasei protejate

dublu x, y;

// coordonate

public:

// membrii clasei publice

Coord() ( x = 0 ; y = 0 ; )

// constructorul clasei de bază

void Input();

// declară o funcție non-virtuală

virtual void Print();

// declară o funcție virtuală

void Coord::Input()

// vă permite să introduceți coordonatele de la tastatură

cout<<"\tx=";

// introduce valoarea x de la tastatură

cout<<"\ty=";

// introduce valoarea y de la tastatură

void Coord::Print()

// afișează valorile coordonatelor pe ecran

cout<<"\tx="<

Clasa de puncte derivată

clasa Dot: publicCoord

// moștenitorul clasei de coordonate

nume char ;

// numele punctului

public:

// membrii clasei publice

Dot (ch ar N): Coord () (nume = N ; )

// apelează constructorul clasei de bază

void Input();

void Print();

void Dot::Input()

// vă permite să introduceți coordonatele punctului de la tastatură

char S ="Introduceți coordonatele punctului";

CharToOem(S, S);

cout<

Coord::Input();

void Dot::Print()

// afișează pe ecran valorile coordonatelor punctului

char S = "Coordonatele punctului";

CharToOem(S, S);

// convertește caracterele șir în chirilice

cout<

// afișează titlul și numele punctului

Coord::Print();

// apelează funcția clasei de bază

clasa Vec: publicCoord

Clasă vectorială derivată

// moștenitorul clasei de coordonate

nume char [3];

// nume vector

public:

// membrii clasei publice

Vec (char * pName): Coord () ( strncpy (nume , pName , 3) ​​​​; nume [ 2 ] = "\0" ; )

void Input();

// suprascrie o funcție non-virtuală

void Print();

// suprascrie o funcție virtuală

void Vec::Input()

// vă permite să introduceți proiecții vectoriale de la tastatură

Cursul 9 Funcții virtuale 3

char S = "Introduceți proiecțiile vectorului";// declară și inițializează șirul prompt

CharToOem(S, S);

// convertește caracterele șir în chirilice

cout<

// afișează promptul și numele vectorului

Coord::Input();

// apelează funcția clasei de bază

void Vec::Print()

// afișează pe ecran valorile proiecțiilor vectoriale

char S = "Proiecții ale unui vector";

// declară și inițializează linia antetului

CharToOem(S, S);

// convertește caracterele șir în chirilice

cout<

// afișează titlul și numele vectorului

Coord::Print();

// apelează funcția clasei de bază

În exemplul de mai sus, sunt declarate o clasă de bază Coord și două clase derivate Dot și Vec. Funcția Print() din clasele derivate este virtuală deoarece este declarată virtuală în clasa de bază Coord. Funcția Print() din clasele derivate Dot și Vec suprascrie funcția clasei de bază. Dacă clasa derivată nu furnizează o implementare suprascrisă a funcției Print(), se folosește implementarea implicită din clasa de bază.

Funcția Input() este declarată non-virtuală în clasa de bază Coord și suprascrisă în clasele derivate Dot și Vec.

void main()

Coord* pC = nou Coord();

// declară un pointer către coordonate și alocă memorie

Dot* pD = new Dot(„D”);

// declară un pointer către un punct și alocă memorie

Vec* pV = nou Vec("V");

// declară un pointer către un vector și alocă memorie

pc->Intrare () ;

pc->Print () ;

// apelează funcția virtuală Coord::Print()

// pointerul către coordonate primește adresa unui obiect de tip punct

pc->Intrare () ;

// apelează funcția non-virtuală Coord::Input()

pc->Print () ;

// apelează funcția virtuală Dot::Print()

// pointerul către coordonate primește adresa unui obiect de tip vectorial

pc->Intrare () ;

// apelează funcția non-virtuală Coord::Input()

pc->Print () ;

// apelează funcția virtuală Vec::Print()

În exemplul de mai sus, indicatorul de coordonate pC ia alternativ valorile adresei obiectelor de coordonate, un punct și un vector. Deși tipul pointerului PC nu se schimbă, apelează diferite funcții virtuale în funcție de valoarea sa.

Când utilizați un pointer de clasă de bază care indică de fapt către un obiect de clasă derivată, este apelată o funcție non-virtuală a clasei de bază.

Trebuie remarcat faptul că operația de atribuire pC = pD, care utilizează operanzi de diferite tipuri (Coord* și Dot*) fără conversie, este posibilă doar pentru indicatorul clasei de bază din partea stângă. Operația de alocare inversă pD = pC nu este validă și provoacă o eroare de sintaxă.

Când este executat, programul afișează:

Coordonatele punctului D:

Proiecții ale vectorului V:

Când apelați o funcție folosind pointeri și referințe, se aplică următoarele reguli:

un apel la o funcție virtuală este rezolvat în funcție de tipul obiectului a cărui adresă este stocată de pointer sau referință;

un apel la o funcție non-virtuală este rezolvat în funcție de tipul de indicator sau referință.

Funcțiile virtuale sunt apelate numai pe obiecte care aparțin unei anumite clase. De aceea

Nu puteți declara o funcție globală sau statică virtuală. Cuvântul cheie virtual poate

Late Bondage Cu componente COM

Înainte ca executabilul client să poată apela metodele și proprietățile unui obiect component, trebuie să cunoască adresele de memorie ale acelor metode și proprietăți. Există două tehnologii diferite pe care programele client le pot folosi pentru a determina aceste adrese.

Programele conectate timpurii învață adrese la începutul procesului de compilare/execuție — în momentul compilării. Când un program de legare timpurie este compilat, compilatorul folosește biblioteca de tipuri a componentei pentru a include adresele metodelor și proprietăților componentei în executabilul client, astfel încât adresele să poată fi accesate foarte rapid și fără erori.Tehnologiile de interoperabilitate COM discutate astfel folosiți legare timpurie.

La rândul lor, programele cu legare tardivă învață adresele proprietăților și metodelor târziu în procesul de compilare/execuție. chiar în momentul în care aceste proprietăţi şi metode sunt numite. Codul legat tardiv accesează de obicei obiectele client prin tipurile de date subiacente, cum ar fi obiectul și utilizează mediul de rulare pentru a determina în mod dinamic adresele metodelor. Deși codul legat tardiv permite utilizarea unor tehnici complexe de programare, cum ar fi polimorfismul, acesta vine cu unele costuri asociate, pe care le vom vedea în curând.

Dar mai întâi, să vedem cum se realizează legarea tardivă folosind reflectarea în C# (Reflectarea este o modalitate pe care codul îl folosește în timpul rulării pentru a determina informații despre interfețele claselor de pe partea serverului; vezi capitolul 5.)

Când legați cu întârziere la un obiect COM într-un program C#, nu trebuie să creați un RCW pentru componenta COM. În schimb, metoda clasei GetTypeFromProgID a clasei Type este apelată pentru a instanția un obiect care reprezintă tipul de obiect COM. Clasa Type este un membru al spațiului de nume System.Runtime.InteropServices și în codul de mai jos configurăm un obiect Type pentru aceeași componentă de acces la date COM folosită în exemplele anterioare:


Tastați objCustomerTableType;

Când există un obiect Type care încapsulează informații despre tipul unui obiect COM, acesta este utilizat pentru a crea o instanță a obiectului COM în sine. Acest lucru se realizează prin trecerea unui obiect Type la metoda CreateInstance a clasei Activator.CreateInstance creează o instanță a unui obiect COM și returnează o referință cu legare tardivă la acesta, care poate fi stocată într-o referință de tip obiect.

obiect objCustomerTable;
objCustomerTable = Activator.CreateInstance(objCustomerTableType);

Din păcate, nu este posibil să apelați metode direct pe o referință de tip object . Pentru a putea accesa un obiect COM, trebuie să utilizați metoda InvokeMember a obiectului Type care a fost creat mai întâi. Când metoda InvokeMember este apelată, i se transmite o referință la un obiect COM împreună cu numele metodei COM care este invocată, precum și o matrice de obiecte tip a tuturor argumentelor primite ale metodei.

ObjCustomerTableType.InvokeMember(„Șterge”, BindingFlags.InvokeMethod, null, objCustomerTable, aryInputArgs);

Să ne amintim din nou succesiunea de acțiuni:

1. Creați un obiect Type pentru un tip de obiect COM utilizând metoda clasei Type.GetTypeFromProgID() .

2. Utilizați acest obiect Type pentru a crea un obiect COM utilizând Activator.CreateInstance() .

3. Metodele sunt invocate pe un obiect COM apelând metoda InvokeMember pe obiectul Type și trecând o referință la obiect ca argument de intrare. Mai jos este un exemplu de cod care combină toate acestea într-un singur bloc:

folosind System.Runtime.InteropServices;
Tastați objCustomerTableType;
obiect objCustomerTable;
objCustomerTableType=Type.GetTypeFromProgID ("DataAccess.CustomerTable");
objCustomerTable=Activator.CreateInstance(ObjCustomerTableType);
objCustomerTableType.InvokeMember("Șterge", BindingFlags, InvokeMethod, null, objCustomerTable, aryInputArgs);
objCustomerTableType = Type.GetTypeFromProgID ("DataAccess.CustomerTable");

Deși caracteristicile de legare tardivă C# evită dificultățile RCW, există unele dezavantaje asociate cu acesta.

În primul rând: legarea tardivă poate fi periculoasă. Când se utilizează legarea timpurie, compilatorul poate interoga biblioteca de tipuri a componentei COM pentru a se asigura că toate metodele apelate pe obiectele COM există efectiv. În cazul legăturii cu întârziere, nu există nimic care să împiedice o greșeală de tipar în apelul către InvokeMember() să provoace o eroare de rulare.

Ultima actualizare: 02/04/2019

Anterior, am analizat două moduri de a schimba funcționalitatea metodelor moștenite de la o clasă de bază - ascunderea și suprascrierea. Care este diferența dintre aceste două metode?

Trece peste

Să luăm un exemplu cu modificarea metodei:

Clasa Persoană ( șir public Prenume ( obține; set; ) șir public Nume ( obține; set; ) Persoană publică (șir prenume, șir Prenume) ( Prenume = prenume; Nume = prenume; ) public virtual void Display() ( Console.WriteLine ($"(Prenume) (Nume)"); ) ) class Angajat: Persoană ( public șir Companie ( get; set; ) public Employee(șir Prenume, șir Nume, șir companie) : bază(Prenume, Prenume) ( Companie = companie; ) public override void Display() ( Console.WriteLine($"(FirstName) (LastName) rulează în (Companie)"); ) )

Să creăm și un obiect Employee și să-l transmitem unei variabile de tip Persoană:

Persoana tom = nou angajat ("Tom", "Smith", "Microsoft"); tom.Display(); // Tom Smith lucrează la Microsoft

Acum obținem un rezultat diferit față de ascunderea. Iar când se apelează tom.Display() se execută implementarea metodei Display din clasa Employee.

Pentru a lucra cu metode virtuale, compilatorul generează un tabel de metode virtuale (Virtual Method Table sau VMT). Adresele metodelor virtuale sunt scrise în el. Fiecare clasă are propriul său tabel.

Când este creat un obiect de clasă, compilatorul transmite cod special constructorului obiectului care conectează obiectul și tabelul VMT.

Și când este apelată o metodă virtuală, adresa tabelului său VMT este preluată din obiect. Adresa metodei este apoi preluată de la VMT și controlul este transferat acestuia. Adică, procesul de selectare a implementării unei metode este efectuat în timpul execuției programului. Acesta este de fapt modul în care se execută o metodă virtuală. Vă rugăm să rețineți că, deoarece mediul de rulare trebuie mai întâi să obțină adresa metodei dorite din tabelul VMT, acest lucru încetinește ușor execuția programului.

Ascundere

Acum să luăm aceleași clase Persoană și Angajat, dar în loc să suprascriem, folosim ascunderea:

Clasa Persoană ( șir public Prenume ( obține; set; ) șir public Nume ( obține; set; ) Persoană publică (șir prenume, șir prenume) ( Prenume = prenume; Nume = prenume; ) public void Display() ( Console.WriteLine( $"(FirstName) (LastName)"); ) ) class Angajat: Persoana ( public string Companie ( get; set; ) public Employee(string firstName, string lastNume, string company) : base(firstName, lastName) ( Compania = firma ; ) public new void Display() ( Console.WriteLine($"(FirstName) (LastName) rulează în (Companie)"); ) )

Și să vedem ce se întâmplă în următorul caz:

Persoana tom = nou angajat ("Tom", "Smith", "Microsoft"); tom.Display(); //Tom Smith

Variabila tom reprezintă tipul Persoană, dar stochează o referință la obiectul Employee. Cu toate acestea, la apelarea metodei Display, versiunea metodei care este definită în clasa Person va fi executată, și nu în clasa Angajat. De ce? Clasa Employee nu înlocuiește metoda Display moștenită de la clasa de bază, ci definește de fapt o nouă metodă. Prin urmare, atunci când tom.Display() este apelat, este apelată metoda Display din clasa Person.