Răspuns :
daca pui aceasta intrebare, presupun ca tu cunosti deja bazele programarii in obiecte in c++. Asadar, o sa ma refer doar la mostenire si ce este cuvantul cheie virtual
in codul de mai sus, clasa B mosteneste clasa A: ceea ce inseamna ca orice membri ar fi in clasa A(fie ca este vorba de o variabila/camp fie ca este vorba de o functie/metoda) este la randul ei folosita si de obiectul B.
deci metodele s,v si w pot fi folosite si de un obiect B.
Pe langa aceasta, obiectul din clasa B poate sa foloseasca metodele pe care le mosteneste de la A si sa le dea o alta definitie, sau un alt sir de instructiuni. Asta se numeste polimorfism. In exemplul tau, vedem ca prin polimorfism sunt modificate metodele v si w, s ramane definita doar in clasa A
Ca sa beneficiezi de toate campurile si metodele unui obiect trebuie sa ai o referinta catre acel obiect. Asta faci prin instructiunea b=new B(). Mai intai se executa partea din dreapta, un nou obiect cu toate caracteristicile clasei B este creat in memoria heap, apoi creezi o referinta b catre acel obiect. In c++, b este un pointer, o variabila care stocheaza adresa de memorie unde se afla inceputul datelor din acel obiect.
Pe de alta parte, o referinta la un obiect din clasa de baza, cea care a fost mostenita, poate sa fie si el egalat cu o referinta a unei clase mostenite, pentru ca tot ceea ce stie sa faca clasa mostenitoare stie sa faca si referinta a.
De aceea tu in cod ai instructiunea
A *a,B *b;
a=b;
Dar pe de alta parte, daca ai incerca sa faci
b=a; ti-ar da eroare de compilare. Pentru ca tu nu poti sa spui ca o referinta specializata pe o clasa mostenitoare stie sa faca tot ceea ce face clasa de baza, tocmai pentru ca referinta b este specializata.
Dar ce se intamplaa atunci cand faci instructiunile
A *a,B *b;
a=b;
a.v();
Pentru ca in acest moment, nu stim ce metoda v va porni programul. Metoda v se afla atat in clasa A cat si in B, iar pointerul a, desi initial a fost definit ca un poiner catre obiect A, acum a fost egalat cu o referinta catre un obiect de tip B. Cum va alege compilatorul?
Si aici intervine cuvantul: VIRTUAL. Toate metodele care sunt initializate cu virtual nu vor fi executate in timpul compilariii ci dupa ce au fost toate clasele deja compilate si metoda trebuie executata in RUNTIME(la EXE)
astfel, daca ai definitia
public class A{
public virtual v(){...}
};
el chiar daca compileaza clasa, ajunge la virtual si atunci zice: stai putin asta e o metoda virtuala, astept pana cand vad unde este definita in alta clasa si apoi o execut.
De aceea iti apare primul sir:
B:v()
A::s()
B::w()
pentru ca tu de fapt nu folosesti metoda din clasa A, ci pe cea din clasa B, stiind ca cea din A este virtuala. De aceea nu-l pacaleste ca ai facut declaratia
a=b; si stie exact ce metoda v trebuie folosita
Daca vei scoate cuvantul virtual din clasa A si faci definitia
public class A{
public v(){...}
};
o sa vezi ca o sa iti apara:
A:v()
A::s()
B::w()
referinta a chiar daca este egala cu b, metoda v() a fost compilata o data cu referinta a din clasa A si pe aceea o va folosi, nestiind ca este virtuala.
Urmatoarele 3 randuri care iti sunt generate si care sunt identice cu primele, apar dupa instructiunea
a=(* A) b;
Instructiunea de mai sus tot ce face este sa faca acelasi lucru precum a=b: convertesc pointerul de clasa A pentru referinta a, si apoi il egalez cu b
Este ceva similar cu
A a*,B b*;
a=(a=b);
deci e normal sa iti dea aceleasi linii.
Ultimele 3 linii sunt generate dupa comanda
(A)(*b).v(); si observi ca aici ne apar doar metode folosite de A
A::v()
A::s()
A::w()
Asta se intampla pentru ca aici NU FACEM o conversie de REFERINTA, ci facem o conversie de referinta obiect. Ii spunem ca referinta b va fi de acum incolo una de tip A. Este echivalenta cu redeclararea pointerului
A b*;
si din cauza asta nu mai exista o suprapunere de pointeri din clase diferite, si stie ca metodele din A trebuie executate.
Poti testa chestia asta si pe tipuri: exista libraria typeinfo care iti spune tipul unei variabile. Daca este integer, este i, daca este pointer integer, Pi, pointer float este Pf... pentru urmatorul cod, va fi urmatoarele outputuri
int* a;
float* c;
cout<<typeid(a).name()<<endl;//Pi
cout<<typeid(c).name()<<endl;//Pf
cout<<typeid((int *)(c)).name()<<endl;//Pi
cout<<typeid((int)(*c)).name()<<endl;//i
primele 2 sunt normale, sunt tipurile pointerelor. In a treia linie, am facut o conversie de referinta de la pointer float, la pointer integer, de aceea apare Pi, iar in a patra imi apare i de la integer, pentru ca acum c nu mai este un pointer, o referinta, ci este o valoare dintr-o variabila.
o sa vezi de asemenea ca la tipuri de date nu poti sa egalezi referintele intre ele, nici macar cand egalezi incerci sa ii dai pointerului float pe cel de tip int, tocmai pentru ca nu au acelasi comportament precum clasele mostenitoare fata de clasa baza.
Ma rog, sper sa fie suficient de clar
in codul de mai sus, clasa B mosteneste clasa A: ceea ce inseamna ca orice membri ar fi in clasa A(fie ca este vorba de o variabila/camp fie ca este vorba de o functie/metoda) este la randul ei folosita si de obiectul B.
deci metodele s,v si w pot fi folosite si de un obiect B.
Pe langa aceasta, obiectul din clasa B poate sa foloseasca metodele pe care le mosteneste de la A si sa le dea o alta definitie, sau un alt sir de instructiuni. Asta se numeste polimorfism. In exemplul tau, vedem ca prin polimorfism sunt modificate metodele v si w, s ramane definita doar in clasa A
Ca sa beneficiezi de toate campurile si metodele unui obiect trebuie sa ai o referinta catre acel obiect. Asta faci prin instructiunea b=new B(). Mai intai se executa partea din dreapta, un nou obiect cu toate caracteristicile clasei B este creat in memoria heap, apoi creezi o referinta b catre acel obiect. In c++, b este un pointer, o variabila care stocheaza adresa de memorie unde se afla inceputul datelor din acel obiect.
Pe de alta parte, o referinta la un obiect din clasa de baza, cea care a fost mostenita, poate sa fie si el egalat cu o referinta a unei clase mostenite, pentru ca tot ceea ce stie sa faca clasa mostenitoare stie sa faca si referinta a.
De aceea tu in cod ai instructiunea
A *a,B *b;
a=b;
Dar pe de alta parte, daca ai incerca sa faci
b=a; ti-ar da eroare de compilare. Pentru ca tu nu poti sa spui ca o referinta specializata pe o clasa mostenitoare stie sa faca tot ceea ce face clasa de baza, tocmai pentru ca referinta b este specializata.
Dar ce se intamplaa atunci cand faci instructiunile
A *a,B *b;
a=b;
a.v();
Pentru ca in acest moment, nu stim ce metoda v va porni programul. Metoda v se afla atat in clasa A cat si in B, iar pointerul a, desi initial a fost definit ca un poiner catre obiect A, acum a fost egalat cu o referinta catre un obiect de tip B. Cum va alege compilatorul?
Si aici intervine cuvantul: VIRTUAL. Toate metodele care sunt initializate cu virtual nu vor fi executate in timpul compilariii ci dupa ce au fost toate clasele deja compilate si metoda trebuie executata in RUNTIME(la EXE)
astfel, daca ai definitia
public class A{
public virtual v(){...}
};
el chiar daca compileaza clasa, ajunge la virtual si atunci zice: stai putin asta e o metoda virtuala, astept pana cand vad unde este definita in alta clasa si apoi o execut.
De aceea iti apare primul sir:
B:v()
A::s()
B::w()
pentru ca tu de fapt nu folosesti metoda din clasa A, ci pe cea din clasa B, stiind ca cea din A este virtuala. De aceea nu-l pacaleste ca ai facut declaratia
a=b; si stie exact ce metoda v trebuie folosita
Daca vei scoate cuvantul virtual din clasa A si faci definitia
public class A{
public v(){...}
};
o sa vezi ca o sa iti apara:
A:v()
A::s()
B::w()
referinta a chiar daca este egala cu b, metoda v() a fost compilata o data cu referinta a din clasa A si pe aceea o va folosi, nestiind ca este virtuala.
Urmatoarele 3 randuri care iti sunt generate si care sunt identice cu primele, apar dupa instructiunea
a=(* A) b;
Instructiunea de mai sus tot ce face este sa faca acelasi lucru precum a=b: convertesc pointerul de clasa A pentru referinta a, si apoi il egalez cu b
Este ceva similar cu
A a*,B b*;
a=(a=b);
deci e normal sa iti dea aceleasi linii.
Ultimele 3 linii sunt generate dupa comanda
(A)(*b).v(); si observi ca aici ne apar doar metode folosite de A
A::v()
A::s()
A::w()
Asta se intampla pentru ca aici NU FACEM o conversie de REFERINTA, ci facem o conversie de referinta obiect. Ii spunem ca referinta b va fi de acum incolo una de tip A. Este echivalenta cu redeclararea pointerului
A b*;
si din cauza asta nu mai exista o suprapunere de pointeri din clase diferite, si stie ca metodele din A trebuie executate.
Poti testa chestia asta si pe tipuri: exista libraria typeinfo care iti spune tipul unei variabile. Daca este integer, este i, daca este pointer integer, Pi, pointer float este Pf... pentru urmatorul cod, va fi urmatoarele outputuri
int* a;
float* c;
cout<<typeid(a).name()<<endl;//Pi
cout<<typeid(c).name()<<endl;//Pf
cout<<typeid((int *)(c)).name()<<endl;//Pi
cout<<typeid((int)(*c)).name()<<endl;//i
primele 2 sunt normale, sunt tipurile pointerelor. In a treia linie, am facut o conversie de referinta de la pointer float, la pointer integer, de aceea apare Pi, iar in a patra imi apare i de la integer, pentru ca acum c nu mai este un pointer, o referinta, ci este o valoare dintr-o variabila.
o sa vezi de asemenea ca la tipuri de date nu poti sa egalezi referintele intre ele, nici macar cand egalezi incerci sa ii dai pointerului float pe cel de tip int, tocmai pentru ca nu au acelasi comportament precum clasele mostenitoare fata de clasa baza.
Ma rog, sper sa fie suficient de clar
Vă mulțumim că ați accesat site-ul nostru dedicat Informatică. Sperăm că informațiile furnizate v-au fost utile. Dacă aveți întrebări sau aveți nevoie de asistență suplimentară, nu ezitați să ne contactați. Vă așteptăm cu drag să reveniți și nu uitați să ne salvați la favorite!