Sunteți pe pagina 1din 10

L5 MANEVRAREA DINAMIC A OBIECTELOR UNEI CLASE FUNCII PRIETENE (FRIEND).

CLASE FRIEND Pentru gestionarea dinamic a memoriei, n C++ exist doi operatori new i delete mai evoluai, din punct de vedere al domeniului de aciune, dect funciile malloc( ) i free( ) din C. Spre deosebire de funcia malloc(), operatorul new tie, fr a i se specifica, ct memorie trebuie alocat fiecrui tip de obiect i poate produce apelul unui constructor. Exemplu: n loc de: int*p=(int*)malloc(sizeof (int)); //funcia de alocare dinamic de memorie n C vom scrie: int*p=new int; // alocare dinamic de memorie prin operatorul new n C++ sau int*p=new(int); // new acioneaz ca funcie n C++

Operatorul new, n general, este utilizat n interiorul constructorilor. Operatorul new apeleaz un constructor, pentru a crea un obiect, atunci cnd, iniial, valoarea atribuit lui this, n cadrul acelui constructor, este NULL. Un obiect poate fi creat n mod: a) static ( fr a se aloca memorie cu operatorul new); b) dinamic (cu sau fr alocare de memorie); n cazul creerii unui obiect n mod dinamic fr alocare de memorie, pointerul this al unui nou obiect va fi setat la o valoare ce reprezint un pointer la un obiect de acelai tip, deja existent. Orice modificare efectuat asupra unor membri ai noului obiect va modifica membrii corespunztori ai celuilalt obiect referit. Cu alte cuvinte, se poate spune c este vorba de o redenumire a obiectului care deja exista.

Un masiv(tablou) de obiecte poate fi iniializat: a) n momentul declarrii;

Exemplu: Class tt { int j; public: tt(int k){j=k;} // constructor int ret( ){return j;} }; void main( ) { tt tab[4]={11,12,13,14}; // iniializare for(int k=10;k<14;k++) cout<<tab[k].ret( )<<\n; } Exemplu: //un tablou de obiecte de tip punct class punct{ public: int u,v; . 57

}; punct tab[5]; punct tab[5]={{0,0},{5,7},{10,17},{8,23},{3,23}}; //init. tabloului n momentul declarrii sale b) n cadrul declarrii, cu ajutorul unei liste de instaniere avnd ca elemente constructori ai obiectului; Dac clasa obiectului de baz accept restriciile cu privire la tipul de instaniere, atunci se va crea o list de instaniere n vederea iniializrii tabloului de obiecte. Identificarea constructorilor se va face n funcie de lista de instanieri. Exemplu: Class t_2 { int k,j; public: t_2(int u, int v){k=u; j=v;} int ret( ){return k;} int ret( ){return j;} }; void main( ) { t_2 t[4]={t_2(11,12), t_2(13,14), t_2(15,16), t_2(17,18)}; int k; for( k=10;k<17;k++) { cout<<t[k].ret( ); cout<<,; cout<<t[k+1].ret( )<<\n; } }
b

Exemplu: class punct { public: int u,v; punct(int a, int b){u=a; v=b;} punct(int a){u=a;v=a;} }; punct tab [5]={ 0, // se apelez constructorul cu un parametru: punct(0) punct(3,5), //se apeleaz constructorul cu doi parametri: punct(3,5) 10, // se apeleaz constructorul cu un parametru: punct(10) .. }; Un constructor implicit (fr parametri) este util la instanierea unui tablou de obiecte, avnd n vedere situaia n care este imposibil transmiterea de parametri necesari constructorului. n prezena constructorului implicit, se pot face instanieri pariale ale unui obiect.

Exemplu: 58

punct tab[5];// nu exist lista de iniializari Exemplu: class punct { public: int u,v; punct(int a, int b){u=a; v=b;} punct(int a){u=a;v=a;} punct( ){u=0; v=0;} }; punct tab [5]={ 0, punct(3,5)}; //iniializare parial Aciunea operatorului new n cazul tablourilor: Sintaxa: id_tablou=new tip_tablou[dim];

Adresa returnat de operatorul new va fi adresa primului obiect din tablou. Exemplu: int*q=new int[5]; //Care este valoarea lui q?

Prin declaraia punct*q=new punct(5,8); se indic o rezervare de memorie pentru obiectul precizat, dup care se va apela constructorul pentru acest obiect. Se pot declara tablouri de obiecte, incluznd alocarea de memorie prin operatorul new. Exemplu: punct*tab=new punct[5];

Aa dup cum am menionat, pentru a se realiza transmiterea de parametri, clasa obiectelor ce formeaz tabloul, trebuie s conin constructorul implicit. Analog funciei free( ) din C, n C++ exist operatorul delete care are ca scop eliberarea zonei de memorie alocat dinamic cu operatorul new. Operatorul delete este prezent n cadrul destructorilor. nainte de a elibera zona de memorie alocat unui obiect, se va apela destructorul su. Cu operatorul delete se poate realiza o eliberare selectiv a spaiului alocat dinamic cu operatorul new. Exemplu: int*p=new int[12]; // se aloc un spaiu de memorie pentru 12 elemente ntregi delete p; // se elibereaz spaiul ocupat doar de obiectul p[0] delete[ ] p; // se elibereaz tot spaiul alocat

Exemplu: class ir { char *s; int dim; public: ir(char*); //constructor (prototip) ~ir( ); //destructor (prototip) }; 59

// definirea constructorului ir::ir(char*t) { dim=strlen(t)+1; s=new chardim; //alocare de memorie strcpy(s,t); } // definirea destructorului ir::~ir( ) { delete s; } . // exemplu de instaniere ir secvena("Secia Informatic"); // se distruge obiectul, apelnd indirect destructorul delete secvena; // apelul direct al destructorului se face atunci cnd identificatorul lui este precedat de iden_clas // i operatorul "::" Exemplu: secventa.ir::~ir( );

Pentru a se evita o recursivitate infinit, nu se va folosi funcia exit ntr-un destructor. Cnd se apeleaz funcia exit, n timpul execuiei unui program, vor fi apelai doar destructorii variabilelor globale, nu i ai variabilelor locale.

Aplicaie: // Crearea i utilizarea unor tablourilor statice i dinamice de obiecte. #include<iostream.h> #include<string.h class salariat { private: char*nume; long sal; void init(char*s=" ",long a=0){ nume=new char[strlen(s)+1]; strcpy(nume,s); } public: salariat(char*s,long a){init(s,a);} salariat(void){init( );} ~salariat ( ) {delete nume;} void introduc_salariat(char*s,long a){init(s,a);} void afis_salariat( ){cout<<"\n Nume"<<nume<<"\t salariu:"<<sal;} }; class serv{ int nr_sal; salariat*sal; public: serv (int nr){ sal=new salariat[nr]; if(!sal){ cout<<"\n"<<nr<<"prea muli"; 60

return; } nr_sal=nr;} ~serv( ){delete[nr_sal]sal; } void introd_serv( ); void afis_serv( ); int NR_SAL( ){return nr_sal;} }; void serv::introd_serv( ){ char nume[25];long sal; for(int i=0;i<nr_sal;i++){ cout<<"Nume:"; cin>>nume; cout<<"Salariu:";cin>>sal; } }

Elementele protejate dintr-o clas formeaz, dup cum am mai precizat, aa numita implementare a tipului abstract definit prin acea clas. O funcie membr a acelei clase se apeleaz ntotdeauna prin obiectul curent, dup cum urmeaz: ident_obiect.ident_funcie_memb (); sau pointer_id_clas->id_funcie_memb( );

Funciile obinuite nu admit astfel de apeluri, datele prelucrate de ele fie c sunt transferate prin parametri, fie c sunt date globale. De aceea, o astfel de funcie, pentru a prelucra obiectele unei clase, trebuie s devin mai nti funcie membr a acelei clase. Pentru o rezolvare mai comod a acestei situaii, s-a cutat s se gseasc nite funcii, care, dei nu sunt funcii membre ale clasei, s aib acces la elementele protejate ale acesteia. Funciile prietene(friend ) sunt funcii asociate unor clase. Ele nu sunt funcii membre ale clasei la ai crei membri are acces. Aceste funcii ignor caracterul privat i protected al membrilor clasei creia i este prieten. Clasa cuprinde doar prototipurile funciilor prietene ei. Exemplu: //S considerm un tip definit de utilizator:

#include <iostream.h> class X { int u,v; public: X(int k, int q){u=k;v=q;} int dif(){return u*u-v*v;} }; void main() { X z(6,3); int p=z.dif(); cout<<"p="<<p; } Se obine pentru p valoarea 27, folosind funcia membr, dif( ). La acelai rezultat se va ajunge folosind funcia friend dif( ) a clasei X, care nu mai este funcie membr a acestei clase. #include <iostream.h> class X { 61

int u,v; public: X(int k, int q){u=k;v=q;} friend int dif(X w); }; int dif(X w){return w.u*w.u-w.v*w.v;} void main() { X z(6,3); cout<<dif(z); } Funcia dif( ) are acces deplin la membrii particulari ai clasei X (la u i v). Apelul ei se face n mod obinuit.

Declararea unei funcii friend se face incluznd prototipul ei, precedat de cuvntul cheie friend, n acea clas. Indiferent de poziia declarrii unei astfel de funcii, n corpul clasei, ea va fi considerat public. O funcie friend, nefiind funcie membr a clasei a crei prieten este, nu poate s aib un nume de obiect al clasei repective. n definiia sa, nu mai apare cuvntul cheie friend. Utilizarea funciilor friend aduc o serie de avantaje: a) la suprancrcarea unor tipuri de operatori [vezi L5]; b) ofer o facilitate n crearea anumitor tipuri de funcii de intrare/ieire; c) sunt necesare cnd dou sau mai multe clase conin membri corelai cu alte seciuni din program. Am remarcat faptul c orice funcie membr nu poate fi funcie friend aceleiai clase, dar este posibil s fie funcie prieten unei alte clase. De aceea se spune c funciile friend constituie o legtur ntre clase. Exist dou situaii n declararea funciilor friend ale unei clase:

a) se specific faptul c funcia membr a unei clase este funcie friend a unei alte clase. Mai exact, se declar funcia membr din prima clas ca fiind de tip friend n a doua clas.

Exemplu: class Y; // se predeclar clasa care conine funcii prietene ale unei alte clase X class X { void F1(Y &a); // funcie membr a clasei X . }; class Y { friend void X::F1(Y &a); //funcie friend pentru clasa Y .. }; b) se poate declara o ntreag clas prieten , n care toate funciile membre ale sale sunt funcii prietene ale unei alte clase. Exemplu: 62

class Y; class X { .. void F1(Y &a); .. }; class Y { .. friend X; // se declar clasa X ca prieten a clasei Y(toate funcile membre // ale clasei X sunt funcii friend pentru clasa Y) .. };

Clasele friend sunt utile atunci cnd este necesar o comunicare ntre clase, care au acelai nivel ierarhic. n aceast situaie, clasa friend are acces la membrii protejai prezeni n cealalt clas care pot include nume de tipuri, enumerri de constante. Membrii clasei la care se permite accesul nu devin membri ai clasei friend.

Aplicaie: S se implementeze o stiv de caractere (ca list simplu nlnuit) folosind o clas ataat nodurilor din list i una ataat stivei respective. class stiva; //predeclarare a clasei care conine ca funcii membre, funcii prietene // unei alte clase class nod { private: friend stiva; //clas friend nod(int i, nod*n); int inf; nod *prec; }; class stiva{ private: nod*vrf; public: stiva( ){vrf=NULL;} ~stiva( ); void push(int c); int pop( ); }; Clasa nod este o clas privat. Accesul la membrii ei se face prin intermediul unor funcii friend (clasa stiva).

Funciile friend nu posed pointerul this.

Punei n eviden caracteristicile programului urmtor. #include<iostream.h> 63

#include<conio.h> class punct{ float x,y; public: punct(float a=0,float b=0){x=a;y=b;} ~punct(){cout<<"Se distruge punct! \n";} void afis() { cout<<"\n Punct de coordonate x="<<x<<" y="<<y;} }; class vector { punct o,v; public: vector(float x_1=0,float y_1=0,float x_2=0, float y_2=0):o(x_1,y_1),v(x_2,y_2){} ~vector(){cout<<"\n Se distruge vector!";} void afis(){cout<<"\n Vector:"; o.afis(); v.afis(); }}; class triunghi{ punct *p_1,*p_2,*p_3; public: triunghi(float a_1=0,float b_1=0,float a_2=0, float b_2=0,float a_3=0,float b_3=0) { p_1=new punct(a_1,b_1); p_2=new punct(a_2,b_2); p_3=new punct(a_3,b_3); } ~triunghi(){ delete p_1; delete p_2; delete p_3; } void afis() { cout<<"\n TRIUNGHI:"; p_1->afis(); p_2->afis(); p_3->afis(); } }; void main(void) { clrscr(); punct p,p_1(3.5,5.6); p.afis(); p_1.afis(); vector v_1,v_2(1.5,2.5,3.5,4.5); vector v_3(2,3); v_1.afis(); v_2.afis(); 64

v_3.afis(); triunghi t_1,t_2(1.2,3.4,5.6); t_1.afis(); t_2.afis(); getch(); } Aplicaie: Folosind funcii friend, s se calculeze o putere a unui numr complex. #include <iostream.h> #include <conio.h> #include <math.h> #define PI 3.14159265358979 class complex { double re; double im; public: double modul; double arg(); void afis(); friend void cp(complex&,int); void init(double x,double y) { re=x; im=y; modul=sqrt(re*re+im*im); } }; inline void complex::afis(){cout<<re<<"+i*"<<"("<<im<<")";} double complex::arg() { if(re==0.0&&im==0.0)return 0.0; if(im==0.0) if(re>0.0) return 0.0; else return PI; if(re==0.0) if(im>0.0)return (PI/2); else return (3*PI)/2; double a=atan(im/re); if(re<0.0)return PI+a; if(im<0.0)return 2*PI+a; return a; } void cp(complex &z,int n) { double r; double a; r=z.modul; a=z.arg(); double r_la_n=pow(r,(double)n); double aa=n*a; 65

z.re=r_la_n*cos(aa); z.im=r_la_n*sin(aa); } void main() { clrscr(); complex u; u.init(-1,-1); cout<<"\n U="; u.afis(); cout<<"\n Modulul nr.U:"<<u.modul; cout<<"\n Nr. ridicat la putere:"; cp(u,3); u.afis(); } Tematica propus 1. S se defineasc o clas PERS i s se ordoneze dup id_obiect o list de instanieri ale acestei clase. Utilizai operatorii de alocare/eliberare de memorie (heap). 2 S se utilizeze funcii friend cu scopul de a implementa o list dublu nlnuit. n lista creat s se efectueze operaii ca: inserarea unui nod, tergerea unui nod,tergerea listei. 3. Se consider n puncte n plan (n>3), necoliniare, date prin coordonatele lor carteziene. S se realizeze un program C++ prin care s se determine un poligon convex cu vrfurile n astfel de puncte, iar cellalte puncte s se afle fie pe laturile poligonului, fie n interiorul su. Folosii funcii friend n tipul class pe care l definii. 4. Fie dreapta din plan Ax+By+C=0 (A 0 sau B 0). Se consider n>0 triplete (Ak,Bk,Ck) care reprezint coeficienii ecuaiilor a n drepte Dk(0<k<n+1). S se realizeze un program C++ prin care s se determine clasele de echivalen induse de relaia de paralelism. S se afieze clasele cu elementele lor. Folosii n tipul abstract definit, funcii friend. 5. Se citesc n vectori cu componente ntregi de lungimi Lk (0<k<n+1).S se determine subsecvenele de elemente consecutive, de lungime maxim.

66

Evaluare