C
Functii virtuale. clase abstracte.FUNCTII VIRTUALE. CLASE ABSTRACTE.Functiile virtuale sunt necesare in reimplementarea unor functii membre ale unor clase de baza atat static cat si dinamic. O functie virtuala este o functie a carei declaratie este precedata de cuvantul cheie virtual. Sintaxa declaratiei: virtual tip_retur id_functie (lista_par_f) ; Functia virtuala se declara in clasa de baza si este redefinita de o clasa derivata Redefinirea unei functii virtuale intr-o clasa derivata pune in evidenta operatiile efective pe care le executa functia. tip_retur id_functie(lista_par); O functie membra definita ca functie virtuala intr-o clasa de baza ramane virtuala in toate clasele derivate de la aceasta clasa de baza. Exemplu: class Punct //definirea functiei virtuale in clasa de baza class patrat:public Punct // functia virtuala in clasa derivata }; void main( ) Se stie ca polimorfismul este admis de C++ atat in faza de compilare cat si in cea de executie. Dupa cum am mai precizat, polimorfismul in timpul compilarii este pus in evidenta prin functii si operatori supraincarcati [L10, L11, L12], iar polimorfismul in timpul executiei se realizeaza pe baza mostenirii si functiilor virtuale. In mod normal, functiile virtuale se comporta la fel ca orice alta functie membra a clasei respective. Capabilitatea lor de a accepta polimorfismul reprezinta modul lor de comportare cand sunt apelate printr-un pointer. Un pointer al clasei de baza poate fi folosit pentru a indica orice clasa derivata din acea clasa de baza. Daca un astfel de pointer indica spre un obiect derivat care contine o functie virtuala, atunci C++ determina care dintre versiunile functiei sa fie apelata (in functie de tipul obiectului pointat). Cand sunt indicate obiecte diferite, sunt executate diferite versiuni ale functiei virtuale. Exemplu: class B }; class D_1:public B class D_2:public B main( ) Se observa ca selectarea versiunii functiei virtuale vf( ), care se va executa, este stabilita de tipul obiectului spre care pointeaza pointerul p. Desi se poate apela o functie virtuala in mod obisnuit folosind identificatorul obiectului si operatorul de accesare '.', polimorfismul in timpul rularii este permis doar daca accesul se face printr-un pointer al clasei de baza. Cu alte cuvinte, daca folosim sintaxa d_1.vf(); se va apela functia vf( ) din clasa derivata D_1, dar nu se va mai profita de avantajul functiei virtuale vf( ). Redefinirea unei functii virtuale pastreaza identic prototipul sau, deci nu este vorba despre o supraincarcare a functiei respective [L10]. Legaturile unei functii virtuale cu un obiect al clasei derivate se realizeaza in mod dinamic (in timpul executiei) si nu static. Exemplu: #include<iostream.h> class dist virtual void time( ) // Ce se intampla daca nu ar fi functie virtuala? class dist_fin:public dist // explicati rezultatul obtinut void time( ) }; void main( ) Caracteristici ale unei functii virtuale: O functie virtuala nu poate fi functie membra statica a clasei de baza respective. O functie virtuala nu poate fi functie friend. Functiile constructor nu pot fi functii virtuale. Functiile destructor pot fi functii virtuale.
O clasa care contine o functie virtuala se mai numeste si clasa polimorfica. O functie virtuala isi pastreaza acest atribut indiferent de cate ori este mostenita. Asadar, daca o clasa derivata, care a mostenit de la clasa de baza o functie virtuala,devine o clasa de baza pentru o alta clasa derivata, functia virtuala poate fi in continuare redefinita. Functia virtuala se poate redefini intr-o ierarhie de forma: clasa_baza->clasa_derivata. Exemplu: class B }; class D_1:public B class D_2:public D_1 main( ) Daca o clasa derivata dintr-o clasa de baza, nu redefineste functia virtuala prezenta in clasa de baza, atunci, cand un obiect din clasa derivata solicita acces la functia respectiva, se va folosi functia definita in clasa de baza. Exemplu: class B }; class D_1:public B }; class D_2:public B main( ) Mai exact, in C++ mostenirea este ierarhizata. Accesul la o functie virtuala, pentru un obiect al unei clase derivate in care nu s-a facut redefinirea functiei virtuale respective, consta in accesarea primei redefiniri a sa in ordinea inversa derivarii. Exemplu: #include<iostream.h> class rad class St:public rad class Dr:public St void main( ) Corelarea intre functii virtuale si alte functii virtuale sau nonvirtuale: Functile virtuale nu pot fi supraincarcate [L10] dar pot apela astfel de functii. Functiile virtuale pot apela functii care nu sunt virtuale si invers. Functiile virtuale pot apela alte functii virtuale. Exemplu: # include <iostream.h> class Inf_1 void supliment_adev( ) virtual void supl_turist( ) virtual supl_econ( ) void supl_fin( ) }; class Inf_2:public Inf_1 void supl_econ ( ) void supl_fin( ) void main( ) Pentru a intelege modul de utilizare a functiilor virtuale, trebuie inteles modul de implementare al lor. Se stie ca fiecare functie membra are atasat pointerul this. In cazul functiilor virtuale se utilizeaza o tabela de pointeri catre functii, care se mai numeste si tabela de functii virtuale. Fiecare clasa, care poseda o functie virtuala, are atasata o astfel de tabela. Tabela are o intrare pentru fiecare functie virtuala, incluzand si pe cele mostenite de la clasa de baza. O astfel de intrare consta dintr-un pointer care pointeaza catre functia apelata de functia virtuala corespunzatoare. Un obiect al unei clase, care contine functii virtuale, poseda un pointer memorat in el prin care acceseaza tabela virtuala a clasei respective. De fiecare data, cand este apelata o functie virtuala pentru acel obiect, pointerul va localiza, in tabela, functia corespunzatoare. In exemplul anterior se poate observa urmatoarea conexiune: Inf_1
Inf_2
Tabele virtualeAmbele tabele contin acelasi pointer catre adevarul( ), iar supliment_adev( ) si supl_fin( ) nici nu apar. De regula, asa cum am mai precizat, clasa de baza a unei ierarhii trebuie sa fie cat mai generala, elementele specifice urmand a se adauga la derivarile acesteia. Gradul de generalitate al clasei de baza poate ajunge pana acolo, incat ea nici sa nu mai intervina in crearea obiectelor. Astfel de clase se numesc clase abstracte. Am vazut, ca daca o clasa derivata nu redefineste functia virtuala din clasa de baza corespunzatoare, atunci se va folosi versiunea prezenta in clasa de baza. Exista multe cazuri in care o clasa de baza nu poate defini suficient un obiect pentru a permite sa fie creata o functie virtuala in acea clasa. Altfel zis, este posibil sa nu existe o definitie semnificativa a functiei virtuale in clasa de baza. Un astfel de deziderat se poate inlatura in C++ folosind functii virtuale pure. Functia virtuala, care nu poseda definitie in clasa de baza, se numeste functie virtuala pura. Sintaxa declararii: virtual tip_f id_functie(lista_par)=0; Constructorii si destructorii nu pot fi functii virtuale pure. In cazul unei functii virtuale pure, orice clasa derivata va trebui sa o defineasca. O clasa abstracta contine cel putin o functie virtuala pura. Exemplu: # include <iostream.h> class baza virtual void afis( )=0; //functie virtuala pura class tip_h:public baza }; class tip_o:public baza main( ) Functia afis( ) fiind declarata ca functie vituala pura suntem asigurati de faptul ca toate clasele derivate o vor redeclara ca functie pura sau o vor defini.. Deoarece o clasa abstracta contine functii, pentru care nu exista definitii (functii virtuale pure), nu se pot crea obiecte ale sale. Totusi se pot crea pointeri si referinte catre aceste clase. De aceea clasele abstracte admit polimorfismul in timpul executiei, care consta in alegerea functiei virtuale corespunzatoare de catre pointerii clasei de baza. In C++, o clasa de baza poate fi folosita pentru a defini natura unei interfete cu o clasa generala. Fiecare clasa derivata va introduce operatiile specifice solicitate de tipurile de date folosite de tipul derivat. Definind o clasa abstracta, vom reusi sa stabilim un mod unic de comunicare cu utilizatorul. Unui astfel de mod de comunicare ii va fi atasat un set de metode (functii membre) caracteristice claselor derivate. De aceea, se mai spune, ca utilizarea functiilor virtuale, a claselor abstracte si a polimorfismului in timpul executiei constituie cele mai puternice si flexibile cai de a obtine o interfata unica cu metode multiple. Cu aceasta se realizeaza o ierarhizare de la general la specific. Aplicatie: Sa consideram ierarhia de clase: punct_2D (clasa de baza) si punct_3D (clasa derivata). Analizati aplicatia respectiva si precizati unde s-au utilizat corect functiile virtuale si in ce situatii ele si-au pierdut calitatea de functii virtuale. #include<iostream.h> #include<math.h> #include<conio.h> class punct_2D protected: float r_2D,teta; float modul() float TETA() void afis_x() void afis_y() float ret_x() float ret_y() void afis_r_2D() public: punct_2D(float a,float b) punct_2D() virtual void afis() virtual void transl(float dx,float dy); void punct_2D::transl(float dx,float dy) class punct_3D:public punct_2D protected: float r_3D,f; float modul() float F() public: void afis() void transl(float dx,float dy,float dz) punct_3D():punct_2D(0,0) punct_3D(float a,float b,float c):punct_2D(a,b) void rotatie_oz(int u) }; void main() Tematica propusa Sa se realizeze un program prin care sa se simuleze functionarea simultana/ independenta unui sistem audio-video, folosind si functii virtuale. Sa se realizeze printr-un program, simularea crearii unor ferestre simple, cu chenare, cu sau fara titlu, precum si operatii ca: mutare, stergere. Folositi si functii virtuale, functii virtuale pure in acest sens. Sa se scrie un program in care sa utilizati o clasa abstracta. Explicati utilitatea ei. Sa se stabileasca o ierarhie de clase corespunzatoare in familia conicelor. Folositi functii virtuale intr-o astfel de ierarhie. Sa se stabileasca o ierarhie de clase corespunzatoare in cadrul structurilor algebrice. Folositi functii virtuale intr-o astfel de ierarhie.
|