Sunteți pe pagina 1din 14

L11 SUPRANCRCAREA OPERATORILOR CU FUNCII FRIEND

La suprancrcarea operatorilor folosind funcii friend, se va ine seama de faptul, c o astfel de funcie nu este funcie membr a clasei, i deci nu i se poate asocia un pointer de tip this. De aceea, unei funcii suprancrcate de tip friend operator, i se transmit n mod explicit operanzii. Ca atare, o funcie friend, care suprancarc un operator binar, va conine doi parametri, iar cea, care suprancarc un operator unar, va conine un parametru.

Cnd se suprancarc un operator binar, folosind funcia friend operator, operandul din stnga operatorului este transmis n primul parametru, iar cel din dreapta, n cel de-al doilea parametru al funciei. Exemplu: . class ecran { int x0, y0; public: ecran( ); ecran(int x1, int y1) { x0=x1; y0=y1; } void afis( ) { cout<<x0<<' '; cout<<y0<<"\n"; } friend ecran operator +(ecran op1, ecran op2); // suprancrcare cu funcie friend ecran operator =(ecran op2); }; ecran operator+(ecran op1, ecran op2) { ecran u; u.x0=op1.x0+op2.x0; u.y0=op1.y0+op2.y0; return u; } ecran ecran::operator = (ecran op2) { x0=op2.x0; y0=op2.y0; return *this; // se returneaz obiectul care a generat apelul "op1" } main( ) { ecran ob_1(5,8),ob_2(6,12); ob_1=ob_2+ob_2; ob_1.afis( );

115

return 0; }

Nu se pot suprancrca cu funcii friend operator, operatorii "=", "[ ]", "->" [L12].

Se tie c operatorii "++" i "--" modific operandul asupra cruia acioneaz. Dac se suprancarc un astfel de operator cu o funcie friend operator, atunci operandul va fi transmis prin valoare, deci o funcie friend nu are cum s modifice operandul respectiv, pentru c acestei funcii nu i se transmite pointerul this ctre acest operand ci doar o copie a acestuia. Deci, nici o modificare adus parametrului nu afecteaz operandul care genereaz apelul. Dac se utilizeaz drept parametru al funciei friend operator o referin, atunci orice modificare adus parametrului n interiorul funciei va afecta operandul care a generat apelul. Exemplu: .. class ecran { int x0,y0; public: ecran( ); ecran(int x1, int y1){ x0=x1; y0=y1;} void afis( ) { cout<<x0<< ; cout<<y0<<"\n";} ecran operator =(ecran op2); friend ecran operator ++(ecran &op); friend ecran operator - -(ecran &op); } ecran ecran::operator=(ecran op2) { x0=op2.x0; y0=op2.y0; return *this; } //folosirea referinei n funcia friend ecran operator ++(ecran &op) { op.x0++; op.y0++; return op; } ecran operator - -(ecran &op) { op.x0- -; op.y0 - -; return op; } main( ) { ecran ob_1(30,40), ob_2; ob_1.afis( ); ++ob_1; ob_1.afis( ); ob_2=++ob_1; ob_2.afis( );

116

- - ob_2; ob_2.afis( ); return 0; }

Operatorii << i >> sunt suprancrcai n C++, pentru a se efectua operaii de intrare/ieire, relativ la tipurile incorporate n el. Utilizatorul poate efectuarea operaii de intrare/ieire asupra tipurilor de date create de el, prin suprancrcarea operatorilor "<<" ,">>". Dup cum am precizat n [L.1], n C++, operatorul de ieire "<<" este numit operator de inserie, pentru c el introduce caractere ntr-un stream, iar operatorul de intrare ">>" este numit operator de extragere, pentru c el extrage caractere dintr-un stream. Funciile operator, care suprancarc astfel de operatori, se vor numi funcii de inserie, respectiv funcii de extragere. Utilizatorul i poate crea funcii de inserie proprii cu urmtoarea sintax: ostream& operator<<(ostream& str,tip_clasa obiect) { // corpul funciei de inserie return str; // str identific streamul invocat anterior }

Ostream este o clas derivat din ios care permite operaia de ieire. Funcia de inserie returneaz o referin spre un stream de tipul ostream. Primul parametru al funciei este o referin la streamul de ieire, iar al doilea reprezint obiectul inserat sau o referin ctre obiectul inserat. nainte de ncheiere, funcia de inserie returneaz un stream, ceea ce permite utilizarea ei ntr-un lan de inserii. Dei n corpul unei astfel de funcii de inserie se pot introduce diverse operaii, rolul su esenial este de a introduce informaii ntr-un stream. Exemplu: class fisa { public: char nume[40]; int cod; unsigned vrsta; char sex; char unitatea; fisa(char* n,int c, unsigned v, char* s, char* u ) { strcpy(nume,n); cod=c; vrsta=v; strcpy(sex,s); strcpy(unitatea,u); } }; S definim o funcie de inserie pentru obiecte de tip "fisa": ostream& operator<<(ostream& str, fisa p) { str<<p.nume<<" "; str<<"+"<<p.cod<<"+"; str<<p.vrsta<<"("<<p.sex<<")\n"; str<<p.unitatea; return str; }

117

Trebuie reinut faptul, c funcia de inserie nu este o funcie membr a clasei pentru care se face suprancrcarea, pentru c operandul din stnga este un stream i nu un obiect din acea clas (transmis implicit prin this) care s o apeleze, iar operandul din dreapta este un obiect al acelei clase. Acesta este i motivul pentru care, n clasa respectiv, se declar publice variabilele care vor fi accesate de astfel de funcie, care nu este membr a ei. Pentru ca o funcie de inserie s aib acces la elementele private ale clasei la care se refer, trebuie s fie declarat ca funcie friend. friend ostream&operator<<(ostream&str, tip_clas obiect); S se rescrie, n aceast posibilitate, exemplul anterior.

Funciile de inserie trebuie s fie ct mai flexibile, de aceea, corpul lor trebuie s fie ct mai simplu posibil. Utilizatorul i poate crea funcii proprii de extracie, cu urmtoarea sintax: istream& operator>>(istream& str,tip_clasa& obiect) { //corpul funciei de extracie return str; }

Extractorii (funciile de extracie) returneaz o referin ctre un stream de tipul istream (un stream de intrare). Primul parametru al funciei de extracie trebuie s fie o referin la un stream de tipul istream, iar al doilea parametru este o referin la un obiect al clasei pentru care se realizeaz suprancrcarea extractorului.

Exemplu: //Funcie de extracie pentru clasa "fisa"declarat anterior istream& operator>>(istream& str, fisa& p) { cout<<"\n Introduceti numele:"; str>>p.nume; cout<<"\n Introduceti codul:"; str>>p.cod; cout<<"\n Introduceti vrsta:"; str>>p.vrsta; return str; }

Atunci cnd se dorete o alocare de memorie n condiii mai speciale, se poate apela la suprancrcarea operatorilor new i delete. Sintaxa funciilor care suprancarc operatorii new i delete: void *operator new(size_tip dim) { . // se efectueaz alocarea de memorie return pointer_la_zona_aloc }

118

void operator delete(void*ind) { //se elibereaz zona de memorie indicat de pointerul p } unde "size_tip" reprezint un tip ntreg fr semn, capabil de a reine cea mai mare zon contigu de memorie care poate fi alocat. Parametrul "dim" reprezint numrul de octei necesar pentru a memora, n mod corect, obiectul pentru care se face alocarea dinamic de memorie.

Funcia new suprancrcat va returna un pointer ctre zona de memorie alocat, sau zero n caz de eec. Funcia new suprancrcat poate efectua i alte activiti solicitate. Funcia delete primete un pointer spre zona de memorie pe care trebuie s o elibereze i o elibereaz.

Operatorii new i delete pot fi suprancrcai global, astfel nct, n orice utilizare a lor, s se poat apela versiunea dorit. Suprancrcarea lor se poate face relativ la una sau mai multe clase. Pentru a suprancrca operatorii new i delete relativ la o clas, se declar funciile operator de suprancrcare, ca funcii membre ale clasei respective. Exemplu: class ecran { int x0,y0; public: ecran( ); ecran(int x1, int y1){ x0=x1; y0=y1;} void afis( ) { cout<<x0<<' '; cout<<y0<<"\n"; } void*operator new(size_tip dim); void operator delete(void*p); }; // suprancrcarea lui new relativ la clasa ecran void*ecran::operator new(size_tip dim ) { return malloc(dim); } // suprancrcarea lui delete relativ la clasa ecran void ecran::operator delete(void *p){free p;} main( ) { ecran p1,p2; p1=new ecran(10,32); if( !p1){ cout<<"Eroare la alocare! \n"; exit(1); } p2=new ecran(-10,-32); if(!p2){ cout<<"Eroare la alocare! \n"; exit(1); }

119

p1->afis( ); p2->afis( ); delete p1; delete p2; return 0; }

Cnd operatorii new i delete sunt suprancrcai relativ la o anumit clas, utilizarea acestor operatori asupra oricrui alt tip de date determin efectuarea operaiilor iniiale new i delete. Astfel de operatori suprancrcai se aplic doar acelor tipuri de date pentru care au fost definii.

Se pot redefini global operatorii new i delete prin suprancrcarea lor n exteriorul oricrei declaraii de clase. Cnd sunt suprancrcai global, se ignor new i delete implicii din C++, i se folosesc noii operatori n operaiile de alocare / eliberare de memorie. Exemplu: class ecran { int x0,y0; public: ecran( ){ }; ecran(int x1, int y1){ x0=x1; y0=y1;} void afis( ) { cout<<x0<< ; cout<<y0<<"\n"; } // operator new global void*operator new(size_tip dim) {return malloc(dim);} // operator delete global void operator delete(void*p){free p;} main( ) { ecran * p1,*p2; p1=new ecran(10,32); if( !p1) { cout<<"Eroare la alocare! \n"; exit(1); } p2=new ecran(-10,-32); if(!p2) { cout<<"Eroare la alocare! \n"; exit(1); } float*f=new float; // se folosete new suprancrcat if(!f) { cout<<"Eroare la alocare! \n"; exit(1); } *f=30.30; cout<<*f<<"\n"; p1->afis();

120

p2->afis(); delete p1; delete p2; delete f; //se folosete delete suprancrcat return 0; }

Sintaxa pentru suprancrcarea operatorilor new i delete pentru tablouri de obiecte: void *operator new[ ](size_tip dim) {return pointer_la _zona_mem;} void operator delete [ ](void*p) {// apel automat al destructorului pentru fiecare obiect }

Exemplu: class ecran { int x0,y0; public: ecran( ){ x0=0; y0=0;} ecran(int x1, int y1){ x0=x1; y0=y1;} void afis( ) { cout<<x0<< ; cout<<y0<<"\n"; } void*operator new(size_tip dim); void operator delete(void*p); void *operator new[](size_tip dim); void operator delete[](void*p); }; // new suprancrcat relativ la clasa ecran void*ecran::operator new(size_tip dim) { return malloc(dim); } //delete suprancrcat relativ la clasa ecran void ecran::operator delete (void*p) { free p; } // new suprancrcat relativ la clasa ecran, pentru tablou void*ecran::operator new[](size_tip dim) { cout<<"se aloc memorie pentru tablou folosind new[] propriu \n"; return malloc(dim); } // delete suprancrcat relativ la clasa ecran,pentru tablouri void ecran::operator delete[](void*p) { cout<<"eliberarea zonei de memorie din tablou folosind delete[] propriu \n";

121

free p; } main( ) { ecran *p1,*p2; int k; p1= new ecran(15,25); // se aloc memorie pentru un obiect if( !p1) { cout<<"Eroare la alocare! \n"; exit(1); } p2=new ecran[15]; // se aloc memorie pentru un tablou if(!p2) { cout<<"Eroare la alocare! \n"; exit(1); } p1->afis( ); for(k=0;k<15;k++) p2[k].afis( ); delete p1; // se elibereaz memoria alocat unui obiect delete [ ] p2; // se elibereaz zona de memorie alocat pentru un tablou return 0; } Aplicaie: S se realizeze operaii aritmetice cu numere raionale folosind suprancrcarea operatorilor aritmetici, inclusiv cu funcii friend. #include<iostream.h> #include<conio.h> #include<stdlib.h> #include<math.h> long cmmdc(long n,long m) { if(n==0)return m; else if(m==0)return n; else { n=fabs(n); m=fabs(m); if(n>m) return cmmdc(m,n%m); else return cmmdc(n,m%n); } } class rational { long nn,nm; //nn-numarator, nm-numitor public: rational() {

122

nn=0; nm=1;} rational (long a,long b=1) { if(b==0) { cout<<"\n Numitorul este 0!!!"; exit(1); } else if(b<0){nm=-b; nn=-a;} else {nm=b;nn=a;} } rational(rational& r) { nn=r.nn; nm=r.nm; } ~rational(){} long ret_nn(){return nn;} long ret_nm(){return nm;} void simplifica(); void acc_nm(rational&); rational invers(); friend istream&operator>>(istream&,rational&); friend ostream&operator<<(ostream&,rational); rational operator +(rational&); rational& operator+=(rational&); friend rational operator -(rational r); rational operator-(rational&); rational&operator-=(rational&); rational operator*(rational&); rational& operator*=(rational&); rational operator/(rational&); rational&operator/=(rational&); }; istream& operator>>(istream& stream,rational& r) { stream>>r.nn; stream>>"/"; stream>>r.nm; return stream; } ostream& operator<<(ostream& str,rational r) { str<<r.nn<<'/'<<r.nm; return str; } rational rational::invers() { if(nn!=0) { long t=nn; nn=nm; nm=t; if(nm<0) {

123

nm=-nm; nn=-nn; } } else cout<<"\n Nu se poate aplica!!"; return *this; } rational rational::operator+(rational&r) { rational t; acc_nm(r); t.nn=nn+r.nn; t.nm=r.nm; t.simplifica(); return t; } rational&rational::operator+=(rational&r) { return *this=*this+r; } void rational::simplifica() { long d=cmmdc(nn,nm); if(d<0)d=-d; if(d!=1){ nn/=d; nm/=d; } } void rational::acc_nm(rational&r) { if(nm!=r.nm) { long t=nm; nn*=r.nm; r.nm*=t; r.nn*=t; } } rational operator-(rational r) { rational t; t.nn=-r.nn; t.nm=r.nm; return t; } rational rational::operator-(rational&r) { rational t=-r; return*this=*this+t; } rational&rational::operator-=(rational&r) { return*this=*this-r; }

124

rational rational::operator*(rational&r) { nm*=r.nm; nn*=r.nn; this->simplifica(); return*this; } rational&rational::operator*=(rational&r) { return *this=*this*r; } rational rational::operator/(rational& r) { if(r.nn!=0)return *this=*this*r.invers(); this->simplifica(); return*this; } rational& rational::operator/=(rational&r) { return *this=*this/r; } void main() { rational r(4,-10),r_1(-5,35),r_2; clrscr(); cout<<"\n Operatii cu numere rationale:"; cout<<"\n"<<r<<"+"<<"("<<r_1<<")="; r_2=r+r_1; cout<<r_2; rational p(16,-40),p_1(-30,24); cout<<"\n"<<p<<"-"<<"("<<p_1<<")="; r_2=p-p_1; cout<<r_2; rational s(-1,25),s_1(-10,-7); cout<<"\n"<<s<<"*"<<"("<<s_1<<")="; r_2=s*s_1; cout<<r_2; rational x(-12,-16),x_1(15,-3); cout<<"\n"<<"("<<x<<")"<<"/"<<"("<<x_1<<")"<<"="; r_2=x/x_1; cout<<r_2; } Aplicaie: Utiliznd suprancrcarea operatorilor, s se scrie un program C++, prin care s se realizeze suma a dou polinoame i produsul unui polinom cu o constant. #include<iostream.h> #include<conio.h> #include<stdlib.h> class polinom { double*coef; int grad; public: polinom()

125

{ grad=0; coef=new double[sizeof(double)]; } polinom(int); polinom(polinom&); friend polinom operator*(double a,polinom& p); ~polinom() { delete[]coef; } polinom& operator+(polinom); friend istream& operator>>(istream&,polinom&); friend ostream& operator<<(ostream&,polinom);}; istream&operator>>(istream& str,polinom& p) { cout<<"\n Dati gradul:"; str>>p.grad; cout<<"\n Dati coeficientii:"; for(int k=0;k<=p.grad;k++) str>>p.coef[k]; return str; } ostream& operator<<(ostream& str,polinom p) { cout<<"\n Gradul:"<<p.grad; cout<<"\n Coeficientii:"; for(int k=0;k<=p.grad;k++) { if(k==0) { str<<p.coef[k]; cout<<'+'; } else if(k==p.grad) { str<<p.coef[k]; cout<<"*X^"<<k; } else { str<<p.coef[k]; cout<<"*X^"<<k<<'+'; } } return str; } polinom::polinom(int g) { grad=g; for(int k=0;k<=grad;k++) coef[k]=0; } polinom::polinom(polinom& p) { grad=p.grad;

126

coef=new double[grad+1]; for(int k=0;k<=grad;k++) coef[k]=p.coef[k];} polinom operator*(double a,polinom& p) { int k; polinom q; q=p; for(k=0;k<=p.grad;k++) q.coef[k]=q.coef[k]*a; return q; } polinom& polinom::operator+(polinom p) { polinom max_p,min_p; int max_gr,min_gr; int k; if(grad==p.grad) { max_gr=min_gr=grad; max_p=*this; min_p=p; } else if(grad>p.grad) { max_gr=grad; max_p=*this; min_gr=p.grad; min_p=p; } else { max_gr=p.grad; max_p=p; min_gr=grad; min_p=*this;} polinom t; t.grad=max_gr; t.coef=new double[max_gr+1]; for(k=0;k<=min_gr;k++) t.coef[k]=max_p.coef[k]+min_p.coef[k]; for(k=min_gr+1;k<=max_gr;k++) t.coef[k]=max_p.coef[k]; *this=t; return*this; } void main() { polinom p,q,h; double a; clrscr(); cout<<"\n Introduceti ct.a:"; cin>>a; cin>>p; cin>>q; cout<<"\n Suma polinoamelor:"; h=p+q;

127

cout<<h; cout<<"\n a*q:"; h=a*q; cout<<h; getch(); } Tematica propus


1.

S se implementeze un tip abstract stiv n care operaiile de introducere / extragere de elemente s se realizeze prin suprancrcarea operatorilor binari aritmetici "+" i "-". n acest scop s se utilizeze funciile friend. S se implementeze clasa complex i s se realizeze operaiile aritmetice cunoscute, folosinduv de suprancrcarea operatorilor respectivi cu funcii friend. S se creeze o funcie de inserie pentru obiecte de tipul abstract fia_personal. S se creeze o funcie de extracie pentru obiecte de tipul abstract fia_personal.

2.

3. 4.

5. S se construiasc o ierarhie de clase n care s se utilizeze: a) suprancrcarea operatorilor cu funcii operator membre; b) suprancrcarea operatorilor cu funcii operator friend; c) funcii de inserie/extracie definite de utilizator.

128

Evaluare