Supraîncărcarea operatorilor

Până acum am văzut că tipurile de date definite în maniera OOP pot interacționa prin intermediul funcțiilor membru. Este, de multe ori, mai ușor, însă, ca acestea să poată utiliza, pentru realizarea unor operații simple, operatori. În capitolul anterior am văzut cum constructorii pot fi supraîncărcați, realizând diferite definiții, în funcție de forma necesară. Într-o manieră asemănătoare pot fi supraîncărcați și operatorii. Dacă, în cazul tipurilor fundamentale semnificația acestora este evidentă, în cazul, să spunem, a două obiecte, fiecare cu 3 date membru, ce semnificație ar putea avea operatorul ‘+’ ? Este nevoie, deci, de instrucțiuni care să precizeze clar, ca în cazul celorlalte funcții membru, cum se vor comporta operatorii asupra noilor tipuri de date.

Supraîncărcarea operatorilor se face urmărind sintaxa: type operator semn (parametri){ instructiuni;} Evident, type reprezintă tipul rezultat prin aplicarea operatorului. Semn este semnul grafic ce reprezintă operatorul, ales din tabelul de mai sus. Intrucțiunile sunt scrise ca în cazul oricărei alte funcții, determinând, efectiv, comportamentul operatorului.

Operatori care pot fi supraîncărcați +   -    *    /    =    <    >    +=   -=   *=   /=   <<   >> <<=  >>=  ==   !=   <=   >=   ++   --   %    &    ^    !    |  ~    &=   ^=   |=   &&   ||   %=   []     ,    ->*  ->   new  delete    new[]     delete[] Un operator unar pentru o clasă poate fi implementat ca funcţie membră fără argumente sau ca funcţie nemembră cu un argument. Acest argument trebuie să fie un obiect al clasei sau o referinţă la un obiect al clasei.

Pentru exemplificare, vom supraîncărca operatorul unar ‘!’ pentru a întoarce conjugatul unui număr complex. Definiția poate completa exemplul de mai jos, care construiește o clasă pentru implementarea numerelor complexe. complex operator ! {      complex a;       a.re=re; a.im=-im; return a;   } Operatorii binari pot fi implementați ca funcții membre cu un argument, sau ca funcții nemembre cu două argumente, dintre care unui este obiect al clasei, sau referință la un obiect al clasei.

Vom implementa operatorul ‘+=’ pentru sumare în primul obiect un al doilea obiect de tip complex. Când optăm pentru varianta implementării acestui operator ca funcţie membră cu un singur argument, presupunând că a şi b sunt obiecte ale clasei complex, a += b este tratată ca şi cum am fi scris a.operator+=(b), fiind invocată funcţia membră operator+= declarată astfel: complex& operator+=(complex &a) {       this->re=this->re+a.re; this->im=this->im+a.im; return *this; } Remarcăm în exemplu folosirea cuvântului cheie this. Acesta este un pointer creat automat, la instanțierea unui obiect, care reține adresa acesteia. De cele mai multe ori utilizarea lui nu este necesară, fiind subînțeles. Utilizarea lui poate aduce mai multă claritatea sau poate rezolva diferite probleme, precum cea întâmpinată la definirea operatorului  ‘+=’, unde era necesară returnarea unei valori de tip complex.

Pentru a exemplifica supraîncărcarea operatorilor vom utiliza clasa complex. Vom defini, astfel, pentru numerele complexe, operațiile de adunare, scădere și înmulțire. Desigur, vom supraîncărca și operatorul de atribuire, ‘=’. class complex { private: float re, im; public: complex(void) {       re=0; im=0; }   complex(float x, float y)       { re=x; im=y; }   complex(complex &c) {       re=c.re; im=c.im; }   void scrie {       cout<<re<<'+'<<im<<"*i \n"; }   void operator =(const complex &a) {        re=a.re; im=a.im; }   complex operator +(const complex a)       { complex z;   z.re=re+a.re; z.im=im+a.im; return z;       } complex operator -(const complex a)        { complex z;   z.re=re-a.re; z.im=im-a.im; return z;       } complex operator *(const complex a)       { complex z;   z.re=re*a.re-im*a.im;n z.im=re*a.im+a.re*im; return z;       } complex operator /(const complex a)       { complex z;   z.re=(re*a.re+im*a.im)/(a.re*a.re+a.im*a.im); z.im=(im*a.re-re*a.im)/(a.re*a.re+a.im*a.im); return z;       } }; Potrivit definiției clasei, putem scrie următoarele exemple de utilizare a obiectelor a, b.

complex a(1,1), b(a),suma, diferenta, produs, cat; suma=a+b; diferenta=a-b; produs=a*b; cat=a/b; Evident, pentru a afișa cele patru numere complexe obținute în urma operațiilor, vom folosi metoda scrie, definită mai sus: suma.scrie; produs.scrie; diferenta.scrie; cat.scrie; Așa cum am supraîncărcat operatorii aritmetici, pot fi supraîncărcați și operatorii de inserție ‘>>’, respectiv de extracție ‘<<’, pentru a putea citi și afișa datele membre ale unui număr complex prin intermediul cin și cout. Mai jos redăm definirea acestor operatori, declarați ca “prieteni” ai clasei complex. friend ostream &operator<<(ostream &out, complex a)    { out<>(istream &in, complex &a) {      cout<<"partea reala:\n"; in>>a.re; cout<<"partea imaginara: \n"; in>>a.im; return in; } Exemplul anterior are o dublă importanță, el introducând în discuție termenul de prieten (friend) al unei clase, concept pe care îl prezentăm în cele ce urmează.