Sunteți pe pagina 1din 9

L12 SUPRANCRCAREA UNOR OPERATORI SPECIALI 1. Suprancrcarea operatorului [ ].

Operatorul [ ] este numit subscript sau indice i este util n accesarea elementelor unui tablou (variabile cu indici). Exemplu: char teta[30], c; c=teta[8]; // aici acioneaz operatorul [ ]

Operatorul [ ] este un operator binar. n "teta[8]", teta reprezint primul operand, iar "8" al doilea operand. Se are n vedere c "[ ]" este un operator de prioritate maxim. Numele tabloului este interpretat ca primul operand, deoarece el se poate utiliza ntr-un mod similar cu identificatorul unei variabile simple, adic permite accesul la o dat de un tip predefinit. Primul operand poate fi chiar o expresie cu pointeri a crei valoare reprezint un pointer. Exemplu: int i,j,*p; // Expresii corecte p[i] // primul operand al operatorului [ ] este un pointer p[i+j-5] // idem (p+3)[i*j+7] // primul operand al operatorului [ ] este o expresie cu pointeri . Al doilea operand asupra cruia acioneaz operatorul [ ] este de un tip oarecare.

Expresia de forma id_tablou[exp_ind] este o expresie lvalue, deci se poate afla n partea stng ntr-o atribuire. De aceea, funcia operator de suprancrcare a operatorului [ ] pentru tipuri abstracte, trebuie s fie o funcie membr nestatic (ca i n cazul operatorului "="), care returneaz o referin la elementul selectat prin funcia respectiv. Nu se poate folosi funcie friend pentru suprancrcarea operatorului [ ]. Sintaxa funciei membr operator de suprancrcare a operatorului [ ], adic a funciei operator[ ]( ) este: tip id_clas ::operator[ ](tip_ind....); sau tip& id_clasa::operator[ ](tip_ind){. }

Funcia operator [ ] este o funcie membr n care primul operand trebuie s fie un obiect de tipul clasei din care provine, iar al doilea operand este singurul parametru explicit al funciei de un tip oarecare. Avnd n vedere scopul acestui operator, de a asigura nscrierea indecilor ntr-un tablou, adesea este utilizat tipul int pentru acest al doilea operand. Dac valoarea returnat de funcia operator[ ]( ) este o referin, atunci ea se poate utiliza att ca operand drept, ct i ca operand stng al operatorului de atribuire.

O expresie de forma tab[k] este interpretat ca tab.operator[ ](k). n expresia tab[k], tab reprezint obiectul (o instaniere a clasei id_clasa), iar k, de regul, este o expresie care are tipul tip_indice sau un tip convertibil la acesta. Aceasta nseamn c valoarea expresiei din operatorul de nscriere este transmis funciei operator[ ]( ) prin parametrul su explicit. Pointerul this va indica spre tab, obiectul care a generat apelul.

129

Exemplu: class Biblioteca { private: char* nume_autor; unsigned lung; public: Biblioteca(int ap) {nume_autor=new char[lung=ap];} ~Biblioteca( ){delete [ ] nume_autor;} void operator=(Biblioteca& c); char& operator[ ](unsigned k); // prototipul funciei de suprancrcare a operatorului [ ] }; char& Biblioteca::operator[ ](unsigned k) { if(k>lung-1) k=lung-1; return nume_autor[k]; // Nu este un apel recursiv. De ce? // Se returneaz o referin la un element al tabloului.

Deoarece operator[ ]( ) returneaz o referin ctre un element al tabloului, care corespunde indicelui precizat explicit, el se poate folosi n partea stng a unei asignri, cu scopul de a se putea modifica valoarea elementului respectiv din tablou. Astfel, exist un avantaj cu privire la controlul asupra dimensiunilor unui tablou n C++. Mai exact, implementnd o clas, care conine un astfel de tablou la care se permite accesul, prin funcia operator[ ]( ), se pot intercepta indicii din exteriorul limitei considerate.

Funcia operator [ ]( ) suprancrcat poate returna valoarea corespunztoare valorii parametrului ce reprezint indicele elementului. Exemplu: .. class pardr { int x[3]; public: pardr(int k,int j,int l) { //constructor care iniializeaz un tablou x[0]=k; x[1]=j; x[2]=l; } int operator[ ](int k){return x[k];}: // se returneaz valoarea de tip int }; main( ){ pardr ob(10,15,20); cout<<ob[1]; // se afieaz valoarea 15 return 0;}

2. Suprancrcarea operatorului ( ).

Operatorul ( ) este cunoscut sub denumirea de operator de apel de funcie. O funcie se apeleaz prin: id_funcie (lista_param_efectivi)

130

O astfel de construcie poate fi privit ca un operand. n realitate ea este format din doi operanzi: id_funcie i lista_param_efectivi crora li se aplic operatorul binar ( ). Cel de-al doilea parametru poate s fie i vid. Aceast situaie este caracteristic acestui operator, spre deosebire de ceilali operatori binari, care nu admit operand vid. Apelul unei funcii se poate realiza i folosind o construcie de forma: (*exp)(lista_param_efectivi)

n care id_funcie este nlocuit cu o expresie tip pointer spre funcia respectiv. Un astfel de apel este util atunci cnd nu se cunoate numele funciei ci doar un pointer la ea. Suprancrcarea operatorului ( ), nu creeaz o nou cale de apel. De fapt, se creeaz o funcie operator, creia i se poate transmite un numr arbitrar de parametri. Exemplu: float operator( )(int x, float f, char*s); ob(6,14.14,"Da"); //obiect al clasei considerate

Aceast instruciune de apel se transpune n urmtoarea apelare a funciei operator ( )( ): Exemplu: operator( )(6,14.14,"Da");

Cnd n program se folosete operatorul ( ), parametrii efectivi sunt copiai n parametrii funciei de suprancrcare a operatorului ( ). Obiectul, care genereaz apelul, este indicat de pointerul this. Funcia, care suprancarc operatorul ( ), trebuie s fie o funcie membr nestatic. Nu poate fi funcie friend. Suprancrcarea operatorului () se folosete i la definirea unui iterator. Iteratorii se utilizeaz la tipuri abstracte care conin colecii de elemente ca: liste, arbori etc. Iteratorii sunt utili n consultarea elementelor dintr-o astfel de structur de elemente protejate. O colecie de elemente se poate implementa n mai multe feluri: a. tablou de obiecte de tip obiect cu dimensiune fix sau variabil; b. list de noduri de tip obiect simplu sau dubl nlnuit; c. arbore binar cu noduri de tip obiect etc.

Un iterator se implementeaz ntr-o clas special ataat unui tip abstract care conine o colecie de elemente de un tip oarecare. Elementele de tip obiect se pstreaz ntr-o zon din memoria heap. Exemplu: class colecie { obiect tab[n]; . }; // tipul colecie se implementeaz ca un tablou cu dimensiune fix class colecie { obiect*ansamblu; int max; }; // tipul colecie se implementeaz n mod dinamic Un astfel de iterator se poate implementa ca o list simplu nlnuit, dublu nlnuit, ca un arbore. Exemplu: // suprancrcarea operatorului ( ) ..

131

class exemplu { int x,y; public: exemplu( ){ } exemplu(int l1, int l2) { x=l1; y=l2; } void afis( ) { .. } exemplu operator+(exemplu op2); exemplu operator( )(int k,int j); // prototipul funciei de suprancrcare a operatorului ( ) }; exemplu exemplu::operator( )(int k,int j) { x=k; y=j; return *this; } exemplu exemplu::operator+(exemplu op2) { exemplu t; t.x=op2.x+x; t.y=op2.y+y; return t; } main( ) { exemplu ob1(10,15),ob2(6,6); ob1.afis( ); ob1(8,9); ob1.afis( ); ob1=ob2+ob1(4,5); ob1.afis( ); . return 0; } La suprancrcarea operatorului ( ), se pot folosi parametri de orice tip, i se poate returna o valoare de orice tip n funcie de scopul propus.

3. Suprancrcarea operatorului pointer -> Suprancrcarea operatorului -> se realizeaz printr-o funcie membr nestatic. La suprancrcare, operatorul -> este considerat ca operator unar care se aplic operandului care l precede.

O construcie de forma g->u se va interpreta sub forma (g.operator->( ))->u. De aceea, funcia operator->( ), trebuie s returneze fie un pointer la un obiect ce reprezint o instaniere a clasei n care opereaz, fie un obiect de un anmit tip pentru care este suprancrcat operatorul ->.

132

Explicaia este urmtoarea: g.operator->( ) reprezint un apel de funcie, iar asupra valorii returnate de ctre aceast funcie se va aplica ->u. Ultimul operator -> este fie operatorul standard (cnd funcia returneaz un pointer), fie tot un operator suprancrcat (cnd funcia returneaz un obiect). n construcia: obiect->element; obiect este o instaniere a clasei respective, cel care genereaz apelul. n ceea ce privete element, el trebuie s fie un element accesibil n cadrul obiectului returnat de funcia operator->( ). Exemplu: . class A { public: int k; A*operator->( ){return this;} }; main( ) { A ob; ob->k=20; cout<<ob.k<<" "<<ob->k; // cele dou forme sunt echivalente return 0; } 4. Suprancrcarea operatorului virgul Virgula este un operator binar. Operatorul virgul suprancrcat, pentru a aciona ntr-un mod similar cu aciunea sa normal, trebuie s renune la valoarea din termenul su stng i s atribuie valoarea operaiei, termenului din dreapta. Astfel c ntr-o list, n care virgula este un separator, se va renuna la toi termenii prezeni, mai puin ultimul termen din dreapta. Exemplu: class exemplu { int x,y; public: exemplu( ){ } exemplu(int l1,int l2) { x=l1; y=l2; } void afis( ) { .. } exemplu operator+(exemplu op2); exemplu operator ,(exemplu op2); }; exemplu exemplu::operator ,(exemplu op2) { exemplu t; t.x=op2.x; t.y=op2.y; cout<<op2.x<<" "<<op2.y<<"\n";

133

return t; } exemplu exemplu::operator+(exemplu op2) { exemplu t; t.x=op2.x+x; t.y+op2.y+y; return t; } main( ) { exemplu ob1(10,30),ob2(10,30), ob3(3,3); ob2.afis( ); cout<<"\n"; ob2=( ob1+ob1, ob2,ob3); ob2.afis( ); return 0; } Dei se renun la valorile operanzilor, cu excepia ultimului operand din dreapta, fiecare expresie va fi executat de compilator, semnalnd eventualele erori.

Operandul din partea dreapt este transmis prin this, iar valoarea sa este eliminat de ctre funcia operator,( ). Funcia va returna valoarea din dreapta operatorului.

5. Concluzii cu privire la utilizarea funciilor operator

Suprancrcarea unor operatori, care acioneaz asupra unor date de tipuri predefinite, cu ajutorul funciilor operator, are ca scop posibilitatea acestora de a aciona i asupra unor obiecte de tip abstract. O funcie operator poate fi sau nu, funcie membr a unei clase. Funciile operator membre au cu un parametru mai puin dect cele nemembre, argumentul ascuns fiind dat de pointerul this. La declararea unei funcii operator este obligatoriu a se avea n vedere urmtoarele: 1. aritatea operatorului (operator unar sau binar); 2. poziia ocupat de operator(prefix sau postfix); 3. domeniu de aciune (funcie membr sau nu).

Aceste elemente determin numrul i tipul parametrilor funciei operator. Pentru funciile membre primul operand al unui operator binar devine obiectul, care mpreun cu operatorul respectiv, acioneaz asupra celui de al doilea operand. n cazul operatorilor unari, pentru a se deosebi cei prefixai de cei postfixai, la cei postfixai se mai adaug un parametru suplimentar, simulnd oarecum modul de acionare al acestora. Ca orice funcie C++, funciile operator se pot suprancrca. Pentru ca funcia operator s acioneze ct mai natural asupra obiectului corespunztor, se utilizeaz referina ca parametru. Exist unele restricii cu privire la utilizarea funciilor operator: 1. nu este permis a se introduce noi operatori; 2. nu se permite schimbarea aritii operatorului respectiv; 3. cel puin un parametru al funciei operator trebuie s fie obiect, sau funcia s fie membr a unei clase. 4. nu se pot combina operatorii cu scopul de a da natere la noi operatori;

134

5. funciile operator pentru operatorii =, [ ], ( ), -> trebuie s fie funcii membre nestatice. Aplicaie: Calculai inversa unei matrice ptratice de ordin doi, folosind i o suprancrcare a operatorului "( )". #include<iostream.h> #include<conio.h> #include<math.h> class matrice { private: float matr[2][2]; public: matrice(){}; matrice(float lin_1[2],float lin_2[2]) { matr[0][0]=lin_1[0]; matr[0][1]=lin_1[1]; matr[1][0]=lin_2[0]; matr[1][1]=lin_2[1]; } matrice(float cl_1,float cl_2,float cl_3,float cl_4) { matr[0][0]=cl_1; matr[0][1]=cl_2; matr[1][0]=cl_3; matr[1][1]=cl_4; } matrice(matrice&m); ~matrice(){} float&operator()(int i,int j) { return matr[i][j]; } float det(); matrice inversa(); friend istream&operator>>(istream&,matrice&); friend ostream&operator<<(ostream&,matrice); matrice&operator+(matrice); matrice&operator-(matrice); matrice&operator*(matrice); }; float matrice::det() { return (matr[0][0]*matr[1][1]-matr[0][1]*matr[1][0]); } matrice matrice::inversa() { matrice t(0,0,0,0); if(!(this->det()))return*this; else{

135

t.matr[0][0]=(1/this->det())*matr[1][1]; t.matr[0][1]=-(1/this->det())*matr[0][1]; t.matr[1][0]=-(1/this->det())*matr[1][0]; t.matr[1][1]=(1/this->det())*matr[0][0]; *this=t; return *this; }} matrice::matrice(matrice&m) { int i,j; for(i=0;i<2;i++) for(j=0;j<2;j++) matr[i][j]=m.matr[i][j]; } istream&operator>>(istream& str,matrice&m) { cout<<"\n Dati matricea:"; str>>m.matr[0][0]; str>>m.matr[0][1]; str>>m.matr[1][0]; str>>m.matr[1][1]; return str; } ostream&operator<<(ostream&str,matrice m) { str<<m.matr[0][0]; str<<' '; str<<m.matr[0][1]; str<<"\n"; str<<m.matr[1][0]; str<<' '; str<<m.matr[1][1]; return str; } matrice&matrice::operator+(matrice m) { matrice t(0,0,0,0); int i,j; for(i=0;i<2;i++) for(j=0;j<2;j++) t.matr[i][j]=matr[i][j]+m.matr[i][j]; *this=t; return *this; } matrice& matrice::operator-(matrice m) { matrice t(0,0,0,0); int i,j; for(i=0;i<2;i++) for(j=0;j<2;j++) t.matr[i][j]=matr[i][j]-m.matr[i][j]; *this=t; return *this; } matrice&matrice::operator*(matrice m) {

136

matrice t(0,0,0,0); int i,j,k; for(i=0;i<2;i++) for(j=0;j<2;j++){ t.matr[i][j]=0; for(k=0;k<2;k++) t.matr[i][j]+=matr[i][k]*m.matr[k][j]; } *this=t; return *this; } void main() { matrice a,b,c,d,e,f; clrscr(); cout<<"\n Adunare de matrice:\n"; cin>>a; cin>>b; matrice a_1=a,b_1=b; cout<<a<<"+"<<"\n"<<b<<"=\n"; c=a+b; cout<<c; cout<<"\n Inmultirea de matrice:\n"; cout<<a_1<<"*"<<"\n"<<b_1<<"=\n"; cout<<a_1*b_1; cout<<"\n Inversa si det:\n"; cin>>a; cout<<"determinantul="<<a.det(); cout<<"\n Inversa:\n"<<a.inversa(); } Tematica propus 1. S se creeze o list ale crei elemente s conin informaii cu privire la un student (nr_matricol, nume, an_studii). Identificarea studenilor din list s se fac pe baza urmtoarelor criterii: nr_matricol, nume. S se creeze doi operatori [ ] n care indexul s fie de tipul pointer la int, pointer la char. Aceti operatori vor cuta n list elementul corespunztor, returnnd un pointer la el, dac l identific, sau NULL n caz contrar.
2.

S se implementeze tipul abstract vector prin care s se iniializeze un tablou unidimensional cu ajutorul constructorului corespunztor, iar prin funcia operator [ ]( ), care suprancarc operatorul [ ], s se afieze elementele tabloului care au valoarea egal cu valoarea indicelui su. Funcia respectiv va returna un obiect sau o referin de tipul corespunztor. S se implementeze tipul abstract experiment prin care se localizeaz un obiect n spaiu folosind coordonatele sale carteziene raportate la un sistem de axe rectangular. Obiectul se poate deplasa ntr-un plan paralel cu planul (x,y). Valorile, cu care se vor modifica coordonatele, s fie cunoscute prin suprancrcarea operatorului ( ). Noua poziie, la un moment dat, se va stabili prin suprancrcarea operatorului +. S se afieze noua poziie a obiectului.

3.

4. S se implementeze un iterator ca: o list simplu nlnuit, o list dublu nlnuit, un arbore. 5. S se construiasc exemple n care s se foloseasc suprancrcarea operatorilor -> i virgul.

137

Evaluare