Sunteți pe pagina 1din 6

Programare Orientat pe Obiect Lucrarea de laborator Nr. 6 Funcii virtuale i polimorfism.

n lucrarea precedent s-a ilustrat folosirea cuvntului cheie virtual al limbajului C++, naintea numelui clasei de baz la definirea claselor derivate pentru cazul de motenire multipl, n care o clas era motenit indirect de mai multe ori, evitndu-se astfel crearea de copii multiple care generau ambiguiti. Cuvntul cheie virtual mai este folosit la definirea funciilor virtuale care reprezint un mecanism pentru implementarea polimorfismului n clasele derivate. Dou sau mai multe obiecte sunt polimorfe dac au asemnri, dar totui sunt diferite. Suprancrcarea operatorilor i a funciilor sunt exemple de polimorfism pentru c o singur entitate refer dou sau mai multe caracteristici diferite. Altfel spus, polimorfismul permite definirea unei interfee comune pentru mai multe metode specifice diferitelor funcionaliti. 1. Conversia pointerilor ntre clase de baz i derivate. Funcii virtuale. Conversia unui pointer la o clas derivat n pointer la o clas de baz a acesteia este implicit, dac derivarea este de tip public i nu exist ambiguiti. Rezultatul conversiei este un pointer la subobiectul din clasa de baz al obiectului din clasa derivat. Conversia invers, a unui pointer la o clas de baz n pointer la derivat nu este admis implicit. O astfel de conversie se poate fora explicit prin operatorul de conversie cast. Rezultatul unei astfel de conversii este ns nedeterminat, de cele mai multe ori provocnd erori de execuie. Exemplificare:
class Baza { /* */ }; class Derivata:public Baza { /* */ }; void main(){ Derivata ob_d; Baza* p_ob_b = &ob_d; // corect, conversie implicita Baza ob_b; Derivata* p_ob_d = &ob_b;// eroare la compilare, nu se poate converti // implicit de la class Baza* la class Derivata* Derivata* p_ob_d =(Derivata*)&ob_b; // se compileaza corect, dar //rezultatul este nedeterminat }

Exerciiul 1: Definii o clas Vehicul care conine o funcie membr de afiare a numelui clasei. Din clasa Vehicul derivai o clas Autoturism n care redefinii funcia de afiare. n funcia main(): - Creai cte dou obiecte instane ale celor dou clase, dintre care cte unul prin alocare dinamic de memorie. Apelai funcia de afiare pentru fiecare din cele 4 obiecte. Ce mesaje sunt afiate la rularea programului? - Declarai un pointer la clasa de baz i folosii-l pentru a crea dinamic un obiect de tipul clasa derivat. Apelai funcia de afiare pentru noul obiect. Ce mesaje sunt afiate la rulare? Modificai programul adugnd naintea funciei de afiare (nainte de tipul returnat) din clasa de baz adugai cuvntul virtual. Compilai i rulai din nou programul. Ce mesaje sunt afiate? Observai deosebirile ntre afiri la cele dou rulri ale programului. Un pointer la o clas de baz poate fi folosit pentru referenierea unei ntregi ierarhii de clase. De exemplu, dac refereniem clasa Vehicul ne putem referi la un autoturism (pentru care s-a definit clasa derivat Autoturism), camion, motociclet sau orice alt mijloc de transport. Termenul general de vehicul

poate referenia tipuri diferite, pe cnd un termen mai precis, de exemplu, autoturismul nu poate indica dect un autoturism. Astfel, n C++, un pointer care refereniaz o clas de baz va putea fi folosit i pentru referenierea claselor derivate. Un pointer ctre un obiect din clasa Autoturism, nu va putea fi folosit pentru referenierea clasei Vehicul pentru c este prea specific. O funcie virtual este o funcie care este declarat de tip virtual n clasa de baz i redefinit ntr-o clas derivat. Redefinirea unei funcii virtuale ntr-o clas derivat domin (override) definiia funciei n clasa de baz. Mecanismul de virtualitate se manifest numai n cazul apelului funciilor prin intermediul pointerilor. Atunci cnd o funcie normal (care nu este virtual) este definit ntr-o clas de baz i redefinit n clasele derivate, la apelul acesteia ca funcie membr a unui obiect pentru care se cunoate un pointer, se selecteaz funcia dup tipul pointerului, indiferent de tipul obiectului al crui pointer se folosete (obiect din clasa de baz sau obiect din clasa derivat). Dac o funcie este definit ca funcie virtual n clasa de baz i redefinit n clasele derivate, la apelul acesteia ca funcie membr a unui obiect pentru care se cunoate un pointer, se selecteaz funcia dup tipul obiectului, nu al pointerului. Sunt posibile mai multe situaii: - Dac obiectul este din clas de baz nu se poate folosi un pointer la o clas derivat. - Dac obiectul este de tip clas derivat i pointerul este pointer la clas derivat, se selecteaz funcia redefinit n clasa derivat respectiv. - Dac obiectul este de tip clas derivat, iar pointerul folosit este un pointer la o clas de baz a acesteia, se selecteaz funcia redefinit n clasa derivat corespunztoare tipului obiectului. Acesta este mecanismul de virtualitate i el permite implementarea polimorfismului n clasele derivate. O funcie redefinit ntr-o clas derivat domin funcia virtual corespunztoare din clasa de baz i o nlocuiete chiar dac tipul pointerului cu care este accesat este pointer la clasa de baz. n apelul ca funcie membr a unui obiect dat cu numele lui, funciile virtuale se comport normal, ca funcii redefinite. Nu pot fi declarate funcii virtuale funciile nemembre, constructorii, funciile friend. Funcia declarat virtual n clasa de baz acioneaz ca o descriere generic prin care se definete interfaa comun, iar funciile redefinite n clasele derivate precizeaz aciunile specifice fiecrei clase derivate aa cum se va ilustra n exerciiul urmtor. Exerciiul 2: Entitile cerc, dreptunghi, triunghi sunt corelate ntre ele prin aceea c toate sunt forme geometrice, deci ele au n comun conceptul de form geometric. Pentru a reprezenta un cerc, triunghi sau dreptunghi ntrun program, trebuie ca aceste clase, care reprezint fiecare o form geometric n parte mpreun cu proprietile sale, s aib n comun clasa care reprezint n general o form geometric. S se defineasc o clas de baz Shape care are ca membri : centrul figurii, aria figurii i o funcie de desenare display.
class Shape{ protected: int centerx, centery; public: Shape(); ~Shape(); double aria(){} void display(); };

Derivai din aceast clas, corespunztor pentru cele trei figuri geometrice, clasele Cerc, TriunghiEchilateral i Patrat, i completai pentru fiecare clas n parte datele membre necesare,

constructorii, destructorii, funcia de calcul al ariei i funcia display care va afia numele figurii de desenat. n funcia main()creai cte un obiect cerc, triunghi echilateral i ptrat i afiai aria fiecruia; apoi declarai un vector de 3 pointeri ctre clasa Shape. Folosii aceti pointeri pentru a crea dinamic cte un obiect de tipul claselor derivate i afiai pentru fiecare obiect n parte aria i tipul su. 2. Motenirea funciilor virtuale Atributul virtual se motenete pe tot parcursul derivrii. Cu alte cuvinte, dac o funcie este declarat de tip virtual n clasa de baz, funcia redefinit ntr-o clas derivat este funcie de tip virtual n mod implicit (fr s fie nevoie de declaraie explicit virtual). Acest lucru nseamn c, dac aceast clas derivat servete pentru definirea unei noi derivri, funcia va fi motenit de asemenea de tip virtual. Pot s apar i alte situaii n motenirea funciilor virtuale. De exemplu, dac o funcie virtual nu este redefinit ntr-o clas derivat, atunci se folosete funcia motenit din clasa de baz i pentru obiecte de tip clas derivat. Exerciiul 3: Reluai exerciiul anterior i derivai din clasa Cerc o nou clas, CercColorat, care s conin, n plus, culoarea cercului dat printr-un numr ntreg iar din clasa Patrat derivai o nou clas Cub. n funcia main() creai n mod dinamic un obiect tip CercColorat i un obiect Cub i afiai ariile celor dou obiecte. 3. Destructori virtuali Destructorii pot fi declarai de tip virtual, i aceast declaraie este absolut necesar n situaia n care se dezaloc un obiect de tip clas derivat folosind pointer la o clas de baz a acesteia. Exerciiul 4: Completai programele de la exerciiile 2 i 3 cu instruciuni de afiare a tipului destructorilor i cu instruciuni de tergere a obiectelor create. Ce mesaje sunt afiate la rularea programelor? Mesajele afiate evideneaz faptul c la tergerea unui obiect tip clas derivat creat prin pointer la clasa de baz a fost ters numai un subobiect al acestuia, subobiectul din clasa de baz. Acest lucru face ca memoria liber (heap) s rmn ocupat n mod inutil (cu partea de date a clasei derivate), dei se inteniona eliminarea complet din memorie a obiectului creat. Exerciiul 5: n programele anterioare declarai destructorul din clasa de baz de tip virtual. Ce mesaje se obin la rularea programelor? Declararea destructorului din clasa de baz de tip virtual rezolv problema ocuprii inutile a memoriei: la tergerea obiectelor se apeleaz destructorul clasei derivate dup tipul obiectului nu al pointerului folosit. Destructorul clasei derivate elibereaz partea din clasa derivat a obiectului, dup care (n mod implicit, fr s fie nevoie ca programatorul s specifice acest lucru), este apelat destructorul clasei de baz care elibereaz subobiectul de baz din obiectul dat.

4. Clase abstracte De cele mai multe ori funcia declarat de tip virtual n clasa de baz nu definete o aciune semnificativ i este neaprat necesar ca ea s fie redefinit n fiecare din clasele derivate (de exemplu, funcia aria() n clasa Shape). Pentru ca programatorul s fie obligat s redefineasc o funcie virtual n toate clasele derivate n care este folosit aceast funcie, se declar funcia respectiv virtual pur. O funcie virtual pur este o funcie care nu are definiie n clasa de baz, iar declaraia ei arat n felul urmtor:
virtual tip_returnat nume_functie(lista_arg) = 0;

O clas care conine cel puin o funcie virtual pur se numete clas abstract. Deoarece o clas abstract conine una sau mai multe funcii pentru care nu exist definiii (funcii virtuale pure), nu pot fi create instane (obiecte) din acea clas, dar pot fi creai pointeri i referine la astfel de clase abstracte. O clas abstract este folosit n general ca o clas fundamental, din care se construiesc alte clase prin derivare. Orice clas derivat dintr-o clas abstract este, la rndul ei clas abstract (i deci nu se pot crea instane ale acesteia) dac nu redefinesc toate funciile virtuale pure motenite. Dac o clas redefinete toate funciile virtuale pure ale claselor ei de baz, devine clas normal (ne-abstract) i pot fi create instane (obiecte) ale acesteia. Exerciiul 6: Modificai programele anterioare astfel nct clasa Shape s fie abstract. Ce modificri trebuie aduse programelor pentru obinerea unei funcionri corecte? 5. Un caz practic Un exemplu practic care evideniaz utilitatea funciilor virtuale n general i a funciilor virtuale pure n special este prezentat n continuare:
class Convert{ protected: double x; // valoare intrare double y; // valoare iesire public: Convert(double i){x = i;} double getx(){return x;} double gety(){return y;} virtual void conv() = 0; }; // clasa FC de conversie grade Farenheit in grade Celsius class FC: public Convert{ public: FC(double i):Convert(i){} void conv(){y = (x-32)/1.8;} }; // clasa IC de conversie inch in centimetri class IC: public Convert{ public: IC(double i):Convert(i){} void conv(){y = 2.54*x;} }; void main (){ Convert* p = 0; // pointer la baza

cout<<"Introduceti valoarea si tipul conversiei: "; double v; char ch; cin >> v >> ch; switch (ch){ case 'i': //conversie inch -> cm (clasa IC) p = new IC(v); break; case 'f': //conv. Farenheit -> Celsius (clasa FC) p = new FC(v); break; } if (p){ p->conv(); cout << p->getx() << "---> " << p->gety()<< endl; delete p; } }

Acest program convertete date dintr-o valoare de intrare ntr-o valoare de ieire (din grade Farenheit n grade Celsius, din inch n centimetri). Clasa de baz absrtract Convert este folosit pentru crearea prin derivare a cte unei clase specifice fiecrui tip de conversie de date dorit. Aceast clas definete datele comune, necesare oricrui tip de conversie, de la o valoare de intrare x la o valoare de ieire y. Funcia de conversie conv() nu se poate defini n clasa de baz, ea fiind specific fiecrui tip de conversie n parte; de aceea se declar funcie virtual pur i trebuie s fie redefinit n fiecare clas derivat. n funcia main() se execut o conversie a unei valori introduse de la consol, folosind un tip de conversie (o clas derivat) care se selecteaz pe baza unui caracter introdus la consol. Acesta este un exemplu care pune n eviden necesitatea funciilor virtuale: deoarece nu se cunoate n momentul compilrii tipul de conversie care se va efectua, se folosete un pointer la clasa de baz pentru orice operaie (crearea unui obiect de conversie nou, apelul funciei conv(), afiarea rezultatelor, distrugerea obiectului la terminarea programului). Singura difereniere care permite selecia corect a funciilor, este tipul obiectului creat, care depinde de tipul conversiei cerute la consol.

6. Polimorfismul
Una din caracteristicile importante ale programrii orientate pe obiecte este aceea c permite definirea unei interfee comune pentru mai multe metode specifice diferitelor funcionaliti. Aceast comportare, care asigur simplificarea i organizarea sistemelor de programe complexe, este cunosut sub numele de polimorfism. Polimorfismul introdus prin mecanismul de virtualitate este polimorfism la nivel de execuie, care permite legarea trzie (late binding) ntre evenimentele din program, n contrast cu legarea timpurie (early binding), proprie apelurilor funciilor normale (nevirtuale). Intercorelarea (legarea) timpurie se refer la evenimentele care se desfoar n timpul compilrii i anume apeluri de funcii pentru care sunt cunoscute adresele de apel: funcii normale, funcii suprancrcate, operatori suprancrcai, funcii membre nevirtuale, funcii friend. Apelurile rezolvate n timpul compilrii beneficiaz de o eficien ridicat. Termenul de legare trzie se refer la evenimente din timpul execuiei. n astfel de apeluri, adresa funciei care urmeaz s fie apelat nu este cunoscut dect n momentul execuiei programului. Funciile virtuale sunt evenimente cu legare trzie: accesul la astfel de funcii se face prin pointer la clasa de baz, iar apelarea efectiv a funciei este determinat n timpul execuiei de tipul obiectului indicat, pentru care este cunoscut pointerul la clasa sa de baz. Avantajul principal al legrii trzii (permis de polimorfismul asigurat de funciile virtuale) l constitue simplitatea i flexibilitatea programelor rezultate. Dezavantajul cel mai important este timpul suplimentar necesar seleciei funciei apelate efectiv din timpul execuiei, ceea ce conduce la un timp de execuie mai mare al programelor.

Exerciiul 7: Se d urmtorul program: class B{ public: void f(){ cout<<f() din B\n; } virtual void g(){cout<<g() din B\n; } }; class D1:public B{ public: void f(){ cout<<f() din D1\n; } void g(){cout<<g() din D1\n; } }; class D2:public B{ public: void f(){ cout<<f() din D2\n; } void g(){cout<<g() din D2\n; } }; void fv1(){ B* pb = new B; D1* pd1 = new D1; D2* pd2 = new D2; B* pb1 = pd1; B* pb2 = pd2; // Obiect B, pointer din B* pb->f(); pb->g(); // Obiecte D1, D2, pointeri D1*, D2* pd1->f(); pd2->f(); pd1->g(); pd2->g(); // Obiecte D1, D2, pointeri B*, B* pb1->f(); pb2->f(); pb1->g(); pb2->g(); delete pb; delete pd1; delete pd2; } Observai comportamentul funciilor virtuale cu ajutorul mesajelor ce vor fi afiate la consol ?

S-ar putea să vă placă și