Sunteți pe pagina 1din 9

L10 SUPRANCARCAREA FUNCIILOR I OPERATORILOR

Suprancrcarea funciilor i operatorilor sunt elemente eseniale n programarea C++. Ele ofer o baz important pentru polimorfism n timpul compilrii, dnd o extensibilitate i flexibilitate limbajului.

Suprancrcarea unei funcii nseamn utilizarea aceluiai identificator de funcie pentru cel puin dou funcii cu condiia ca s difere ntre ele prin tipuri i/sau numr de parametri (cu prototipuri diferite). n acest fel compilatorul poate selecta, la un moment dat, funcia care trebuie apelat. Exemplu: n biblioteca C exist funcii cum ar fi: abs( ) care returneaz valoarea absolut a unui ntreg; iabs( ) care returneaz valoarea absolut a unei date de tip float; fabs( ) care returneaz valoarea absolut a unei date de tip double. Aceste funcii realizeaz aceeai aciune dar pentru tipuri diferite de parametri. n C++ se poate folosi un singur identificator pentru funcia respectiv n toate cele trei situaii.

//Se definete funcia abs() n cele trei situaii int abs(int k); double abs(double d); long abs(long l); main( ) { cout<<abs(-345)<<"\n"; cout<<abs(-111.23)<<"\n"; cout<<abs(-7L)<<"\n"; return 0; } int abs(int k) { cout<<"Se folosete funcia abs() pentru ntregi \n"; return k<0?-k:k; } double abs(double d) { cout<<"Se folosete funcia abs() pentru double \n"; return d<0?-d:d;} long abs(long l) { cout<<"Se folosete funcia abs() pentru long \n"; return l<0?-l:l; } Se observ c acelai identificator de funcie abs( ) indic o aciunea general, iar compilatorul, avnd n vedere tipul argumentului, va alege corect metoda particular pentru fiecare situaie. Datorit polimorfismului se realizeaz o reducere de la trei aciuni, la o singur aciune. Exemplu: . void adir(char *s1, char *s2); void adir(char *s1, int k); main ( ) {

106

char ir[50]; strcpy(ir,"V"); adir(ir,"mulumesc ."); cout<<ir<<"\n"; adir(ir,50); cout<<ir<<"\n"; return 0; } //Se vor concatena dou iruri void adir(char*s1, char*s2){strcat(s1,s2);} // Se va concatena un ir cu un ntreg void adir(char*s1,int k) { char t[50]; sprintf(t, "%d",k); strcat(s1,t); } S-a realizat suprancrcarea funciei adir( ) i anume: o versiune concateneaz dou iruri cu funcia strcat( ), iar a doua versiune transform un ntreg ntr-un ir i apoi l adaug la un alt ir. Suprancrcarea este folosit pentru a crea o interfa prin care se adaug la un ir fie un alt ir, fie un ntreg. Desigur, dou funcii suprancrcate pot s difere i prin tipul returnat, dar nu este suficient. Exemplu: // Suprancrcri eronate: a) int func_f(int k); float func_f(int k); // nu difer dect prin tipul returnat

b) void f(int *x); void f(int x[]);

// nu difer prin argument

Conversia automat a tipului, n C++, poate conduce la apariia unor ambiguiti n suprancrcarea funciilor. Exemplu: func_f(double d); cout<<func_f('c'); // este corect pentru c n C++ se realizeaz o conversie automat de la // caracterul 'c' la echivalentul su double. // Totui n unele situaii se pot produce ambiguiti. .. float func_f(float f); double func_f(double f); main( ) { cout<<func_f(15.2)<<" "; // se apeleaz funcia func_f(double) cout<<func_f(56); // compilatorul nu este informat cu privire la convertirea ntregului n // float sau double. return 0; } float func_f(float f){return f;}

107

double func_f(double f){return f;} n C++, la selectarea funciei suprancrcate, se va ine cont de coincidena ntre cele dou liste de parametri (actuali i formali), ca numr i tip. #n cazul, n care o astfel de coinciden nu exist, se va urmri selectarea acelei funcii care va corespunde n urma unor conversii tip parametru actual n tip parametru formal, n ordine, dup cum urmeaz: (1) conversii predefinite (tipuri predefinite), fr trunchieri de informaii; (2) conversii predefinite cu trunchieri de informaii; (3) conversii predefinite ct i conversii ce corespund tipurilor definite de utilizator. Dac n urma acestui proces este posibil selectarea mai multor funcii, compilatorul va semnala eroare.

Conform procesului de motenire, dac CD este o clas derivat a clasei de baz CB, atunci funciile membre ale clasei CB, vor fi funcii membre i ale clasei CD. n general, nseamn c o astfel de funcie poate opera att cu obiecte ale clasei CB ct i cu obiecte ale clasei CD. Exist ns situaii n care o funcie motenit s nu poat fi apelat la obiecte ale clasei derivate. n acest caz se va proceda la suprancrcarea funciei pentru clasa derivat. La o astfel de suprancrcare pot rmne nemodificate identificatorul funciei, tipul i chiar numrul de parametri. Selectarea unor astfel de funcii se va face n funcie de obiectul pentru care sunt apelate. Exemplu: class CB { protected: float u,v; public: CB(float t=0, float z=0) { u=t; v=z; } void afis( ) { cout<<u=<<u<<\n; cout<<v=<<v<<\n; } }; class CD:public CB { protected: float fu,fv; public: CD(float dt=0, float dz=0, float bt=0, float bz=0):CB(bt,bz) { fu=dt; fv=dz; } void afis( ) { ........ ............... } // se vor afia cele patru valori ale obiectului curent , dou motenite de la CB i dou specifice lui CD. };

108

// #n definiia funciei afis( ) din CD se va apela funcia afis( ) din CB cu ajutorul // operatorului de rezoluie de domeniu. void CD::afis( ) { CB::afis( ); // pentru a afia pe u,v cout<<fu<<fv; } Exemple de apel: CB ob(10,20); CD od(10,20,30,40); ob.afis( ); // funcia membr a clasei CB od.afis( ); // funcia membr a clasei CD n afara faptului, c funciile constructor au un rol special n iniializare, ele nu difer de alte tipuri de funcii. De aceea suprancrcarea funciilor constructor se ntlnete foarte des. Suprancrcarea unui constructor permite crearea unui obiect cu cele mai convenabile metode. n C++, se pot suprancrca aproape toi operatorii, astfel nct ei s efectueze diverse operaii n k clase create. La suprancrcarea unui operator, nu se pierde nimic din semnificaiile sale originale, ci doar se extinde tipul obiectelor crora li se poate aplica. Un operator se poate suprancrca prin definirea unei funcii operator. O funcie operator definete operaiile specifice pe care le va efectua operatorul suprancrcat relativ la clasa n care opereaz. Funciile operator pot fi sau nu funcii membre ale clasei n care opereaz. De obicei, funciile operator, care nu sunt funcii membre ale clasei n care opereaz, sunt funcii friend.

Sintaxa unei funcii operator membr a unei clase; tip_returnat iden_clas :: operator id_operator(lista_param) { // operaii} Cuvntul "operator" este un cuvnt cheie, iar la crearea unei funcii operator, id_operator reprezint simbolul operatorului suprancrcat.

Adesea, funciile membre operator returneaz un obiect din clasa asupra creia opereaz. De regul, ele nu sunt funcii statice.

Funcia operator care suprancarc un operator binar conine un singur parametru. Motivul const n aceea c operandul din stnga operatorului binar este plasat implicit funciei prin pointerul this care este ataat funciei operator. Operandul din dreapta operatorului binar este transmis funciei opertor prin parametrul ob2. Apelul funciei operator este generat de obiectul reprezentat de operandul stng al operatorului binar respectiv. Exemplu: class ecran { int x0, y0; public: ecran(int x1, int y1) { x0=x1; y0=y1; } void afis( )

109

{ cout<<x0<<"\n "; cout<<y0<<"\n"; } ecran operator +(ecran op2); }; ecran ecran::operator +(ecran op2) { ecran u; u.x0=op2.x0+x0; u.y0=op2.y0+y0; return u; } main( ) { ecran ob_1(8,10), ob_2(9,15); ob_1.afis( ); // se afieaz 8 10 ob_2.afis( ); // se afieaz 9 15 ob_1=ob_1+ob_2; ob_1.afis( ); // se afieaz 17 25 return 0; } O situaie mai deosebit se ntlnete la suprancrcarea operatorului binar de atribuire (asignare) =. Copierea datelor membre ale unui obiect surs (reprezentat prin operandul drept al operatorului de atribuire) n datele membre corespunztoare ale obiectului destinaie (reprezentat prin operandul stng al operatorului de atribuire), se poate realiza: 1) n faza de iniializare, ceea ce implic i alocare de memorie id_clas ob_2=ob_1; 2) prin asignri de obiecte id_clas ob_1, ob_2; ob_2=ob_1; Iniializarea prin copiere se face cu ajutorul constructorului de copiere. Dac un astfel de constructor nu exist n clasa respectiv, copierea se va face la nivel de bit. Constructorul de copiere nu se apeleaz n situaia 2). Suprancrcarea operatorului de atribuire conduce la o soluionare corect a operaiei de asignare de obiecte. O astfel de suprancrcare se va face n clase care conin date membre pointeri ctre zone de memorie heap (alocare dinamic de memorie), sau exist funcii care au ca parametri obiecte, respectiv returneaz obiecte.

Ca i ceilali operatori binari, suprancrcarea operatorului de atribuire se face cu o funcie membr operator care nu este static. Funcia operator, care se mai poate identifica prin operator=( ), poate avea ca parametru: a) un obiect de tipul clasei respective i returneaz un obiect de acelai tip; id_clas id_clas::operator = (id_clas operand_drept); b) o referin i returneaz o referin la obiectul curent; id_clas & id_clas::operator =(id_clas & operand_drept);

n C++, n procesul de derivare, pentru membrii motenii dintr-o clas de baz, n care s-a produs suprancrcarea operatorului de atribuire, se apeleaz automat funcia operator de suprancrcare. Pentru celelalte date membre se aplic procedeul de copiere la nivel de bit. Dac operatorul de atribuire a fost suprancrcat pentru clasa derivat, funcia operator realizeaz atribuirea obiectului surs obiectului destinaie, n totalitate.

110

Funcia operator pentru suprancrcarea operatorului unar ++(preincrementare) nu conine parametri, operandul su este transmis implicit prin pointerul this. Ca i la operatorul de atribuire, cel care i modific valoarea este operandul care a generat apelul funciei. tip operator ++( ) {....// corpul operatorului preincrementare} Funcia operator pentru suprancrcarea operatorului unar ++(postincrementare) : tip operator++( int x) {. ......................... //corpul operatorului postincrementare, x primete valoarea 0 } Analog pentru operatorii unari de predecrementare, respectiv postdecrementare (--).

Aplicaie: Exemplificai suprancrcarea operatorilor binari - i =, respectiv a operatorului unar de preincrementare. . class ecran { int x0,y0; public: ecran( ); // constructor implicit util n crearea obiectelor temporare ecran (int x1, int y1){x0=x1; y0=y1;} void afis( ) { cout<<x0<<"\n "; cout<<y0<<"\n"; } ecran operator - (ecran op2); ecran operator = (ecran op2); ecran operator ++( ); }; ecran ecran::operator -(ecran op2) { ecran u; u.x0=x0- op2.x0; u.y0=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"modificat } ecran::operator ++( ) { x0++;

111

y0++; return *this; } main( ) { ecran ob_1(5,8),ob_2(6,12), ob_3(10,10); ob_1.afis( ); ob_2.afis( ); ++ob_1; ob_1.afis( ); // se afieaz 6 9 ob_2=++ob_1; ob_2.afis( ); // se afieaz 7 10 ob_1 = ob_2= ob_3; // asignare multipl ob_1.afis( ); // se afieaz 10 10 ob_2.afis( ); // se afieaz 10 10 return 0; } S observm funcia operator "-". Operandul din dreapta operatorului "-" , conform semnificaiei operaiei de scdere, este sczut din operandul stng. Deoarece cel care genereaz apelul funciei operator este obiectul din stnga, datele din ob_2 trebuie sczute din cele corespunztoare indicate prin pointerul this. . Suprancrcarea operatorilor dintr-o asignare compus: Exemplu: ecran ecran::operator +=(ecran op2) { x0=op2.x0+x0; yo=op2.y0+y0; return *this; } La suprancrcarea operatorilor cu funcii membre operator: a) Nu se modific precedena operatorului; b) Nu se modific n-aritatea operatorului;

Aplicaie: Utiliznd suprancrcarea operatorilor "+" i "-" cu funcii membre operator, s se efectueze suma i diferena a doi vectori de aceeai dimensiune. #include<iostream.h> #include<math.h> #include<conio.h> class vector { double v[30]; int n; public: void cit_v(char*t); void afis_v( ); vector operator+(vector &p); vector operator -(vector &q); double norma(void); };

112

void vector::cit_v(char*t) { int m,i; cout<<"DIM=\n"; cin>>m; cout<<"VECTORUL \n"; for(i=1;i<=m;i++){ cout<<t<<""<<i<<"="; cin>>v[i]; } n=m; cout<<"\n"; } void vector::afis_v( ) { int i; for(i=1;i<=n;i++) cout<<i<<":"<<v[i]<<"\n"; } vector vector::operator+(vector& p) { int i; vector r; for(i=1;i<=n;i++) r.v[i]=v[i]+p.v[i]; r.n=n; return r; } vector vector::operator-(vector &q) { int i; vector r; for(i=1;i<=n;i++) r.v[i]=v[i]-q.v[i]; r.n=n; return r; } double vector::norma(void) { int i; double n=0; for(i=1;i<=vector::n;i++) n+=v[i]*v[i]; return sqrt(n); } void main( ) { vector f,g,h; clrscr( ); f.cit_v("a"); g.cit_v("b"); cout<<"a:\n"; f.afis_v( );

113

cout<<"Norma lui a="<<f.norma( )<<"\n"; cout<<"\n"; cout<<"b:\n"; g.afis_v( ); cout<<"Norma lui b="<<g.norma( )<<"\n"; cout<<"\n"; h=f+g; cout<<"\n"; cout<<"c=a+b="; h.afis_v( ); cout<<"\n"; h=f-g; cout<<"d=a-b="; h.afis_v ( ); 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 "-" cu funcii membre operator. S se implementeze tipul abstract vector i s se realizeze operaiile corespunztoare pentru calculul sumei a doi vectori, produsului unui vector cu o constant, normei unui vector. Utilizai suprancrcarea operatorilor binari aritmetici "+" i "*" cu funcii membre operator. S se implementeze clasa complex i s se realizeze operaiile aritmetice cunoscute, folosind suprancrcarea operatorilor respectivi cu funcii membre operator. S se implementeze tipul abstract punct. S se arate c suprancrcarea operatorilor, cu funcii membre operator, pentru obiecte de tip punct, care se motenesc n clasa derivat segment, se poate aplica i obiectelor de tip segment. S se implementeze tipul abstract punct n care s se realizeze citirea i afiarea coordonatelor unui punct, determinarea distanei ntre dou puncte i poziia a trei puncte n plan. S se foloseasc constructorul de copiere i suprancrcarea operatorului de atribuire.

2.

3.

4.

5.

114

Evaluare