Sunteți pe pagina 1din 11

L9 FUNCII VIRTUALE.

CLASE ABSTRACTE

Funciile virtuale sunt necesare n reimplementarea unor funcii membre ale unor clase de baz att static ct i dinamic. O funcie virtual este o funcie a crei declaraie este precedat de cuvntul cheie virtual. Sintaxa declaraiei: virtual tip_retur id_funcie (list_par_f) ; Funcia virtual se declar n clasa de baz i este redefinit de o clas derivat Redefinirea unei funcii virtuale ntr-o clas derivat pune n eviden operaiile efective pe care le execut funcia. tip_retur id_funcie(list_par);

O funcie membr definit ca funcie virtual ntr-o clas de baz rmne virtual n toate clasele derivate de la aceast clas de baz. Exemplu: class Punct { public: float x,y; void punctul(float x0, float y0); virtual float Aria( ){return 0.0;} //definirea funciei virtuale n clasa de baz }; class patrat:public Punct { public: float l4; float Aria( ){return l4*l4;} // funcia virtual n clasa derivat }; void main( ) { Punct p; patrat pt; pt.l4(15.2); Punct *pp; pp=&p; float ap=pp->Aria( ); pp=&pt; float apt=pp->Aria( ); .. } Se tie c polimorfismul este admis de C++ att n faza de compilare ct i n cea de execuie. Dup cum am mai precizat, polimorfismul n timpul compilrii este pus n eviden prin funcii i operatori suprancrcai [L10, L11, L12], iar polimorfismul n timpul execuiei se realizeaz pe baza motenirii i funciilor virtuale. n mod normal, funciile virtuale se comport la fel ca orice alt funcie membr a clasei respective. Capabilitatea lor de a accepta polimorfismul reprezint modul lor de comportare cnd sunt apelate printr-un pointer. Un pointer al clasei de baz poate fi folosit pentru a indica orice clas derivat din acea clas de baz. Dac un astfel de pointer indic spre un obiect

95

derivat care conine o funcie virtual, atunci C++ determin care dintre versiunile funciei s fie apelat (n funcie de tipul obiectului pointat). Cnd sunt indicate obiecte diferite, sunt executate diferite versiuni ale funciei virtuale. Exemplu: .. class B { public: virtual void vf( ) { cout<<"vf este o funcie virtual n clasa de baz B \n"; } }; class D_1:public B { public: void vf ( ){cout<< funcia virtual vf() n clasa derivat D_1 \n";} }; class D_2:public B { public: void vf( ){cout<<" funcia virtual vf() n clasa derivat D_2 \n";} }; main( ) { B *p,b; // se declar un pointer i un obiect al clasei de baz B D_1 d_1; // se declar un obiect al clasei derivate D_1 (instaniere) D_2 d_2; // idem pentru clasa D_2 p=&b; // p pointeaz la clasa B, atribuindu-se ca valoare adresa obiectului b din B p->vf( ); // p acceseaz funcia virtual vf() din clasa de baz B . p=&d_1; // p pointeaz ctre clasa derivat D_1 p->vf( ); // acces la vf() din clasa derivat D_1 .. p=&d_2; // p pointeaz spre clasa derivat D_2 p->vf( ); // acces la vf( ) din clasa derivat D_2 return 0; } Se observ c selectarea versiunii funciei virtuale vf( ), care se va executa, este stabilit de tipul obiectului spre care pointeaz pointerul p.

Dei se poate apela o funcie virtual n mod obinuit folosind identificatorul obiectului i operatorul de accesare ".", polimorfismul n timpul rulrii este permis doar dac accesul se face printr-un pointer al clasei de baz. Cu alte cuvinte, dac folosim sintaxa d_1.vf();

96

se va apela funcia vf( ) din clasa derivat D_1, dar nu se va mai profita de avantajul funciei virtuale vf( ).

Redefinirea unei funcii virtuale pstreaz identic prototipul su, deci nu este vorba despre o suprancrcare a funciei respective [L10]. Legturile unei funcii virtuale cu un obiect al clasei derivate se realizeaz n mod dinamic (n timpul execuiei) i nu static.

Exemplu: #include<iostream.h> class dist { public: double d; dist(double l){d=l;} virtual void time( ) { cout<<"Viteza mobilului este de 60 km/h\n"; cout<<"Timpul necesar:"<<d/60<<"\n"; } // Ce se intampla daca nu ar fi functie virtuala? }; class dist_fin:public dist { public: dist_fin(double d):dist(d){ }// explicati rezultatul obtinut void time( ) { cout<<"Viteza de deplasare este de 100km/h\n"; cout<<"Timp necesar:"<<d/100<<"\n"; } }; void main( ) { dist *p,O(100.0); dist_fin OB(100.0); p=&O; p->time( ); p=&OB; p->time( ); }

Caracteristici ale unei funcii virtuale: 1) O funcie virtual nu poate fi funcie membr static a clasei de baz respective. 2) O funcie virtual nu poate fi funcie friend. 3) Funciile constructor nu pot fi funcii virtuale. 4) Funciile destructor pot fi funcii virtuale. O clas care conine o funcie virtual se mai numete i clas polimorfic.

O funcie virtual i pstreaz acest atribut indiferent de cte ori este motenit. Aadar, dac o clas derivat, care a motenit de la clasa de baz o funcie virtual,devine o clas de baz pentru o alt clas derivat, funcia virtual poate fi n continuare redefinit. Funcia virtual se poate redefini ntr-o ierarhie de forma: clas_baz->clas_derivat.... Exemplu: .. class B

97

{ public: virtual void vf( ) { cout<<"vf este o funcie virtual n clasa de baz B \n"; } }; class D_1:public B { public: void vf( ) {cout<<"redefinirea funciei virtuale vf() n clasa derivat D_1 \n"; } }; class D_2:public D_1 { public: void vf( ){cout<<"motenirea funciei vf() n clasa derivat D_2 \n";} }; main( ) { B *p,b; // se declar un pointer i un obiect al clasei de baz B D_1 d_1; // se declar un obiect al clasei derivate D_1 (instaniere) D_2 d_2; // idem pentru clasa D_2 p=&b; // p pointeaz spre clasa B, atribuindu-se ca valoare adresa obiectului b din clasa B p->vf( ); // p acceseaz funcia virtual vf() din clasa de baz B . p=&d-1; // p pointeaz spre clasa derivat D_1 p->vf( ); // acces la vf() din clasa derivat D_1 .. p=&d_2; // p pointeaz spre clasa derivat D_2 p->vf( ); // acces la vf() din clasa derivat D_2 return 0; } Dac o clas derivat dintr-o clas de baz, nu redefinete funcia virtual prezent n clasa de baz, atunci, cnd un obiect din clasa derivat solicit acces la funcia respectiv, se va folosi funcia definit n clasa de baz. Exemplu: .. class B { public: virtual void vf( ) { cout<<"vf este o funcie virtual n clasa de baz B \n";

98

} }; class D_1:public B { public: void vf( ) {cout<<"aceasta este funcia vf( ) din clasa derivat D_1 \n";} }; class D_2:public B { public: // vf( ) nu este redefinit de clasa derivat D_2 i va fi folosit definiia din clasa //B }; main( ) { B *p,b; // se declar un pointer / un obiect al clasei de baz B D_1 d_1; // se declar un obiect al clasei derivate D_1 (instaniere) D_2 d_2; // idem pentru clasa D_2 p=&b; // p pointeaz spre clasa B, atribuindu-se ca valoare adresa obiectului b din clasa //B p->vf( ); // p acceseaz funcia virtual vf( ) din clasa de baz B . p=&d-1; // p pointeaz spre clasa derivat D_1 p->vf( ); // acces la vf( ) din clasa derivat D_1 .. p=&d_2; // p pointeaz spre clasa derivat D_2 p->vf( ); // acces la funcia vf( ) din clasa de baz B return 0; } Mai exact, n C++ motenirea este ierarhizat. Accesul la o funcie virtual, pentru un obiect al unei clase derivate n care nu s-a fcut redefinirea funciei virtuale respective, const n accesarea primei redefiniri a sa n ordinea invers derivrii. Exemplu: #include<iostream.h> class rad { public: virtual void afis( ){cout<<"Nu exista ascendenti!\n";} }; class St:public rad { public: void afis( ){cout<<"Exista descendent stanga\n";} }; class Dr:public St { public:

99

void afis( ){cout<<"Exista un descendent dreapta al precedentului nod\n";} }; void main( ) { //Dai explicaii asupra mesajelor care se vor afia St n; n.afis( ); Dr n_1; n_1.afis( ); } Corelarea ntre funcii virtuale i alte funcii virtuale sau nonvirtuale: 1) Funcile virtuale nu pot fi suprancrcate [L10] dar pot apela astfel de funcii. 2) Funciile virtuale pot apela funcii care nu sunt virtuale i invers. 3) Funciile virtuale pot apela alte funcii virtuale. Exemplu: # include <iostream.h> class Inf_1 { public: virtual void adevrul ( ) { cout<<"Excursie n Romnia:\n"; supliment_adev( );} void supliment_adev( ) {cout<<"Este o ar cu un potenial turistic \n"; supl_turist( );} virtual void supl_turist( ){cout<<"Relieful ei este variat \n"; supl_econ( );} virtual supl_econ( ){cout<<"Bogiile subterane sunt diverse \n"; supl_fin( );} void supl_fin( ){ cout<<"Optimism \n";} }; class Inf_2:public Inf_1 { public: void supl_turist( ){cout<<"Relieful ei este monoton:\n"; supl_econ( );} void supl_econ ( ){cout<<"Apele curg furios \n"; supl_fin( );} void supl_fin( ){cout<<"Pesimism\n";} }; void main( ) { char c; Inf_1 * mesaj; cout<<"Care variant v intereseaz (O/P)?\n"; cin>>c; if((c=='O') || (c=='1')) mesaj=new Inf_1; else mesaj=new Inf_2; mesaj->adevrul(); delete mesaj;}

Pentru a nelege modul de utilizare a funciilor virtuale, trebuie neles modul de implementare al lor. Se tie c fiecare funcie membr are ataat pointerul this. n cazul funciilor virtuale se utilizeaz o tabel de pointeri ctre funcii, care se mai numete i tabel de funcii virtuale. Fiecare clas, care posed o funcie virtual, are ataat o astfel de tabel. Tabela are o intrare pentru fiecare funcie virtual, incluznd i pe cele motenite de la clasa de baz. O astfel de intrare const dintr-un pointer care pointeaz ctre funcia apelat de funcia virtual corespunztoare.

100

Un obiect al unei clase, care conine funcii virtuale, posed un pointer memorat n el prin care acceseaz tabela virtual a clasei respective. De fiecare dat, cnd este apelat o funcie virtual pentru acel obiect, pointerul va localiza, n tabel, funcia corespunztoare. n exemplul anterior se poate observa urmtoarea conexiune: Inf_1 (*adevrul)( ) void Inf_1::adevrul( ) {} (*supl_turist)( ) void Inf_1::supl_turist( ) {} Inf_2 (*adevrul)( ) (*supl_turist)( ) void Inf_2::supl_turist( ) {} Tabele virtuale (*supl_econ)( ) void Inf_2::supl_econ( ) {} (*supl_econ)( ) void Inf_1::supl_econ( ) {}

Ambele tabele conin acelai pointer ctre adevrul( ), iar supliment_adev( ) i supl_fin( ) nici nu apar.

De regul, aa cum am mai precizat, clasa de baz a unei ierarhii trebuie s fie ct mai general, elementele specifice urmnd a se aduga la derivrile acesteia. Gradul de generalitate al clasei de baz poate ajunge pn acolo, nct ea nici s nu mai intervin n crearea obiectelor. Astfel de clase se numesc clase abstracte. Am vzut, c dac o clas derivat nu redefinete funcia virtual din clasa de baz corespunztoare, atunci se va folosi versiunea prezent n clasa de baz. Exist multe cazuri n care o clas de baz nu poate defini suficient un obiect pentru a permite s fie creat o funcie virtual n acea clas. Altfel zis, este posibil s nu existe o definiie semnificativ a funciei virtuale n clasa de baz. Un astfel de deziderat se poate nltura n C++ folosind funcii virtuale pure. Funcia virtual, care nu posed definiie n clasa de baz, se numete funcie virtual pur. Sintaxa declarrii: virtual tip_f id_funcie(list_par)=0; Constructorii i destructorii nu pot fi funcii virtuale pure. n cazul unei funcii virtuale pure, orice clas derivat va trebui s o defineasc. O clas abstract conine cel puin o funcie virtual pur. Exemplu: # include <iostream.h> class baza { protected: int nr; public:

101

void cit_nr(int m){nr=m;} virtual void afis( )=0; //funcie virtual pur }; class tip_h:public baza { public: void afis( ){cout<<hex<<nr<<\n";} }; class tip_o:public baza { public: void afis( ){cout<<oct<<nr<<"\n";} }; main( ) { tip_h n1; tip_o n2; n1.cit_nr(50); n1.afis( ); n2.cit_nr(50); n2.afis( ); return 0; } Funcia afis( ) fiind declarat ca funcie vitual pur suntem asigurai de faptul c toate clasele derivate o vor redeclara ca funcie pur sau o vor defini..

Deoarece o clas abstract conine funcii, pentru care nu exist definiii (funcii virtuale pure), nu se pot crea obiecte ale sale. Totui se pot crea pointeri i referine ctre aceste clase. De aceea clasele abstracte admit polimorfismul n timpul execuiei, care const n alegerea funciei virtuale corespunztoare de ctre pointerii clasei de baz. n C++, o clas de baz poate fi folosit pentru a defini natura unei interfee cu o clas general. Fiecare clas derivat va introduce operaiile specifice solicitate de tipurile de date folosite de tipul derivat. Definind o clas abstract, vom reui s stabilim un mod unic de comunicare cu utilizatorul. Unui astfel de mod de comunicare i va fi ataat un set de metode (funcii membre) caracteristice claselor derivate. De aceea, se mai spune, c utilizarea funciilor virtuale, a claselor abstracte i a polimorfismului n timpul execuiei constituie cele mai puternice i flexibile ci de a obine o interfa unic cu metode multiple. Cu aceasta se realizeaz o ierarhizare de la general la specific.

Aplicaie: S considerm ierarhia de clase: punct_2D (clas de baz) i punct_3D (clas derivat). Analizai aplicaia respectiv i precizai unde s-au utilizat corect funciile virtuale i n ce situaii ele i-au pierdut calitatea de funcii virtuale. #include<iostream.h> #include<math.h> #include<conio.h> class punct_2D

102

{ private: float x,y; void init_2D(float a=0,float b=0) {x=a;y=b;} protected: float r_2D,teta; float modul() {return sqrt(x*x+y*y); } float TETA() { if(x)return atan(y/x); if(y>0)return M_PI/2; return -M_PI/2; } void afis_x() { cout<<"x="<<x<<"\n"; } void afis_y() { cout<<"y="<<y<<"\n"; } float ret_x(){return x;} float ret_y(){return y;} void afis_r_2D() { cout<<"r_2D="<<r_2D<<"\n"; } public: punct_2D(float a,float b) { init_2D(a,b); r_2D=modul(); teta=TETA(); } punct_2D() { init_2D(); r_2D=teta=0; } virtual void afis() { afis_x(); afis_y(); afis_r_2D(); } virtual void transl(float dx,float dy); }; void punct_2D::transl(float dx,float dy) { x+=dx; y+=dy;

103

r_2D=modul(); teta=TETA(); } class punct_3D:public punct_2D { private: float z; void init_3D(float c=0){z=c;} protected: float r_3D,f; float modul(){ return (pow(punct_2D::modul(),2)+pow(z,2)); } float F() { if(r_2D)return atan(z/r_2D); if(z>0)return M_PI/2; return -M_PI/2; } public: void afis() { punct_2D::afis_x(); punct_2D::afis_y(); cout<<"z="<<z<<"\n"; cout<<"r_3D="<<r_3D<<"\n"; cout<<"f="<<f<<"\n"; } void transl(float dx,float dy,float dz) { punct_2D::transl(dx,dy); z+=dz; r_3D=modul();f=F();} punct_3D():punct_2D(0,0) { init_3D(); r_3D=f=0; } punct_3D(float a,float b,float c):punct_2D(a,b) { init_3D(c); r_3D=modul(); f=F();} void rotatie_oz(int u) { float x,y; x=punct_2D::ret_x(); y=punct_2D::ret_y(); x=x*cos(u*M_PI/180)-y*sin(u*M_PI/180); y=x*sin(u*M_PI/180)+y*cos(u*M_PI/180); } }; void main() { punct_2D xx,yy(11,12),*p; xx.afis();

104

p=&yy; p->afis(); punct_3D g,h(13,23,32),*q; clrscr(); g.afis(); q=&h; q->afis(); q->transl(45,55,65); q->afis(); q->rotatie_oz(74); q->afis();getch();}

Tematica propus 1) S se realizeze un program prin care s se simuleze funcionarea simultan/ independent unui sistem audio-video, folosind i funcii virtuale. 2) S se realizeze printr-un program, simularea crerii unor ferestre simple, cu chenare, cu sau fr titlu, precum i operaii ca: mutare, tergere. Folosii i funcii virtuale, funcii virtuale pure n acest sens.
3) 4)

S se scrie un program n care s utilizai o clas abstract. Explicai utilitatea ei. S se stabileasc o ierarhie de clase corespunztoare n familia conicelor. Folosii funcii virtuale ntr-o astfel de ierarhie. S se stabileasc o ierarhie de clase corespunztoare n cadrul structurilor algebrice. Folosii funcii virtuale ntr-o astfel de ierarhie.

5)

105

Evaluare