Sunteți pe pagina 1din 94

PROGRAMAREA

CALCULATOARELOR 2

Curs 6-7
1
Supraîncărcarea metodelor și
operatorilor
SUPRAÎNCĂRCAREA FUNCŢIILOR ȘI METODELOR
 În general, se consideră că metodele aparțin claselor și
funcțiile sunt exterioare claselor, în limbajul C++, deci
tratarea lor se va face ca atare.

 Supraîncărcarea funcţiilor și metodelor (overloading)


oferă posibilitatea de a scrie un set de funcţii/metode
care, deşi au nume identice, prelucrează în mod diferit
datele primite la intrare

 Pentru ca operaţia să se desfăşoare cu succes,


funcţiile/metodele respective trebuie să difere prin
semnătură, adică prin tipul datelor primite ca parametri,
ordinea lor, numărul lor
2
int myFunction(int x)
{
return x;
}

int myFunction(int x, int y)


{
return x*y;
}

3
 Compilatorul nu face distincţie între un parametru
transferat prin valoare şi unul transmis prin referinţă. În
acest caz trebuie utilizat un parametru suplimentar,
redundant (ce nu va fi folosit de funcţie):

int myFunction(int x){


return x;
}

int myFunction(int &x, int a){


return x;
}
4
 Supraîncărcarea funcţiilor nu poate fi realizată prin
diferenţierea celor două module doar prin tipul valorii
returnate
 Două declaraţii de funcţii de genul:

int myFunction(int x);


double myFunction(int x);

nu vor fi acceptate de către compilator

5
 Supraîncărcări de funcţii şi ambiguităţi
 Ambiguităţile sunt legate de conversia automată a tipului
în C++:
float myFunc(float i);
double myFunc(double i);

 Apel:
cout<< myFunc(1.9f); // fara ambiguitati - double
cout<< myFunc(10); // cu ambiguitati

6
 Metodele unei clase pot fi supraîncărcate la fel ca orice
funcţie
 Uzual supraîncărcarea se foloseşte în cazul
constructorilor:
 de obicei, o metodă constructor este folosită pentru a
iniţializa anumite date din cadrul clasei
 supraîncărcarea permite lucrul cu mai multe tipuri de date,
ce necesită iniţializări distincte

 Nu este supraîncărcare de metode una din situaţiile


următoare:
 două metode/funcții cu acelaşi nume, una independentă şi
alta membră a unei clase
 două metode cu acelaşi nume, fiecare fiind membră a unei
alte clase, fără legătură între acestea
! domeniile de vizibilitate nu se suprapun în aceste cazuri
7
SUPRAÎNCĂRCAREA OPERATORILOR

Noţiuni introductive
 Supraîncărcarea operatorilor presupune atribuirea de noi
sensuri operatorilor, care să extindă aria de aplicabilitate a
lor și pentru alte tipuri de date, inclusiv obiecte din clase.
 Să considerăm că avem o clasă Complex cu metode ce
realizează operațiile de bază și dorim să efectuăm operații
cu numere complexe :

Complex a, b, c, d, e;
e = mulcomplex(adcomplex(&a, &b), sccomplex(&c, &d));

8
Am putea scrie prin supraîncărcarea operatorilor pentru
obiecte de tip Complex, mai simplu, astfel:
e = (a+b)*(c-d) ;

 Prin supraîncărcare, nu se pierde nici unul din


rolurile şi funcţionalităţile iniţiale ale operatorilor

 În C++, aproape toţi operatorii pot fi supraîncărcaţi în


clasele proprii, astfel încât la folosirea lor pe instanţele
acestor clase, rolul jucat să fie cel stabilit de utilizator şi
nu cel predefinit

9
 Nu pot fi supraîncărcaţi operatorii:
 . (punct)
 :: (rezoluţie)
 ?: (condiţional)
 .* (indirectare) (operator doar in C++)
 sizeof()
 Operatori ce pot fi supraîncărcaţi:

+ - * / = < > += -= *= /= << >> <<= >>=


== != <= >= ++ -- % & ^ ! | ~ &= ^= |=
&& || %= [] () , ->* ->
new delete new[ ] delete[ ]
Obs:
.* si ->* operatori pointer la membru - accesul prin obiect, respectiv pointer la
un membru al clasei, prin pointer la membru (C++) 10
 Caracteristici:
 prin supraîncărcare nu se pot introduce operatori noi
 pentru tipurile predefinite nu se admit supraîncărcări
 prin supraîncărcarea operatorilor nu se poate schimba:
 n-aritatea

 prioritatea

 asociativitatea

 operatorii compuşi (+=, -=, …) nu sunt afectaţi de


supraîncărcarea operatorilor de bază (+, -, …):
 pentru aceştia trebuie făcută o supraîncărcare explicită

 dacă un operator poate fi utilizat atât ca operator unar


cât şi binar (&, *, +, -) atunci trebuie făcute
supraîncărcări diferite pentru cele două ipostaze 11
 Operatorii se supraîncarcă prin crearea unor
metode/funcții speciale, ce conţin în nume cuvântul cheie
operator (metodă/funcţie operator)

 O metodă operator defineşte operaţiile specifice pe care


le va efectua operatorul respectiv, relativ la clasa în care a
fost destinat să lucreze

 Metodele/Funcţiile operator pot fi:


 metode membre nestatice ale clasei în care vor opera
 funcţii externe, de obicei funcţii friend:
 cel puţin un argument de tipul clasei sau referinţă la

tipul clasei
12
 Operatorii ->, =, [ ] şi ( ) și cei compuși pot fi
supraîncărcaţi numai cu metode membre

 Operatorii >> (extracție) și << (inserție) se supraîncarcă


doar cu funcții friend.

 Metodele/funcţiile operator nu admit parametri cu valori


implicite

 Cu excepţia operatorului de atribuire (=) (asignare), prin


derivare se moştenesc operatorii supraîncărcaţi în clasa
de bază

 Operatorii supraîncărcaţi în clasa derivată ascund alte


supraîncărcări ale aceloraşi operatori din clasele de bază
13
Supraîncărcarea operatorilor cu metode membre

 Numărul de argumente este egal cu numărul de operanzi


minus unu:
 primul operand fiind referit prin pointerul this în mod
implicit
 operatorii unari nu au argumente
 operatorii binari au un singur argument

 Forma generală:
tip_returnat Nume_clasa::operator#(lista_argumente)
{
// operatii
}
 semnul # este o rezervare de loc pentru operatorul care
urmează să fie supraîncărcat 14
class Complex {
float re, im;
public:
Complex(float x=0, float y=0) {
re = x;
im = y;
}

void showVal(void) {
cout << "Date membre: "<< “re = " << re;
cout << ", im = " << im << "\n";
}

// supraincarcare cu metode membre


Complex operator+(Complex x);
Complex operator-(Complex x); 15
};
Complex Complex :: operator+(Complex x) {
Complex rez;
rez.im = im + x.im;
rez.re = re + x.re;
return rez;
}

Complex Complex :: operator-(Complex x) {


Complex rez;
rez.im = im - x.im;
rez.re = re – x.re;
return rez;
}
16
int main( )
{
Complex unu(5,3), doi(-1,6);
Complex trei;

trei = doi + unu;


// echivalent cu apelul explicit: doi.operator+(unu)
trei.showVal( );

trei = doi - unu;


// echivalent cu apelul explicit: doi.operator-(unu)
trei.showVal( );
}
17
EXEMPLU ŞI CU REFERINŢE:
#include <iostream>
using namespace std;
class Complex { float re, im;
public: Complex(float x=0, float y=0) {
re = x;
im = y;}
void showVal(void) {
cout << "Date membre: "<< "re = " << re;
cout << ", im = " << im << "\n"; }
// supraincarcare cu metode membre
//Complex operator+(Complex x);
Complex operator-(Complex x);
18
Complex& operator+(Complex &);
};//Complex class
/*Complex Complex:: operator+(Complex x) {
Complex rez;
rez.im = im + x.im;
rez.re = re + x.re;
return rez; }
*/
Complex Complex:: operator-(Complex x) {
Complex rez;
rez.im = im - x.im;
rez.re = re - x.re;
return rez; }

Complex& Complex:: operator+(Complex &x) {


Complex &rez= *new Complex(re + x.re, im + x.im);
19
return rez;
}
int main( ){ Complex unu(5,3), doi(-1,6), trei;
cout<< "\nPrimul numar - ";
unu.showVal();
cout<< "\nAl doilea numar - ";
doi.showVal();
/*cout<< "\n Suma (doi + unu) ";
trei = doi + unu;
trei.showVal( );*/
// echivalent cu apelul explicit: Complex &ref_trei=doi.operator+(unu)
Complex &ref_trei=doi + unu;
cout<< "\n Suma (doi + unu) ";
ref_trei.showVal( );
// echivalent cu apelul explicit: doi.operator+(unu)
cout<< "\n Diferenta - (doi - unu) ";
trei = doi - unu; // echivalent cu apelul explicit: doi.operator-(unu)
20
trei.showVal( );
}
Supraîncărcarea operatorilor prin funcţii friend

 În acest caz este nevoie de trimiterea explicită a tuturor


operanzilor necesari deoarece funcţiile friend nu fac parte
din clasă şi deci nu au acces la pointer-ul this:
 operatorii unari au un parametru
 operatorii binari au doi parametri

 Forma generală:
friend tip_returnat operator#(lista_argumente) ;

 semnul # este o rezervare de loc pentru operatorul care


urmează să fie supraîncărcat
21
class Complex{
float re, im;
public:
Complex(float x=0, float y=0) {
re = x;
im = y;
}

void showVal(void) {
cout << "Date membre: "<< “re = " << re;
cout << ", im = " << im << "\n";
}

friend Complex operator+(Complex x, Complex y);


friend Complex operator-(Complex x, Complex y);
}; 22
Complex operator+(Complex x, Complex y)
{
Complex rez;
rez.re = x.re + y.re;
rez.im = x.im + y.im;
return rez;
}

Complex operator-(Complex x, Complex y)


{
Complex rez;
rez.re = x.re - y.re;
rez.im = x.im - y.im;
return rez;
}
23
int main( )
{
Complex unu(5,3), doi(-1,6);
Complex trei;

trei = doi + unu;


// echivalent cu apelul explicit: operator+(doi, unu)
trei.showVal( );

trei = doi - unu;


// echivalent cu apelul explicit: operator-(doi, unu)
trei.showVal( );
}
24
EXEMPLU ŞI CU REFERINŢE:
#include <iostream>
using namespace std;
class Complex { float re, im;
public:
Complex(float x=0, float y=0) {
re = x;
im = y;
}
void showVal(void) {
cout << "Date membre: "<< " re = " << re;
cout << ", im = " << im << "\n";
}
friend Complex operator+(Complex x, Complex y);
friend Complex operator-(Complex x, Complex y);
25
//friend Complex& operator+(Complex &, Complex &);
};//Complex class
Complex operator+(Complex x, Complex y) {
Complex rez;
rez.re = x.re + y.re;
rez.im = x.im + y.im;
return rez; }

Complex operator-(Complex x, Complex y) {


Complex rez;
rez.re = x.re - y.re;
rez.im = x.im - y.im;
return rez;
}
/*Complex& operator+(Complex &x, Complex &y){
Complex *t;
t= new Complex(x.re+y.re, x.im+y.im);
26
return *t;
}*/
int main( ){
Complex unu(5,3), doi(-1,6);
Complex trei;
cout<< "\nPrimul numar - ";
unu.showVal();
cout<< "\nAl doilea numar - ";
doi.showVal();
trei = doi + unu;
// echivalent cu apelul explicit: operator+(doi, unu)
cout<< "\n Suma - (doi + unu) ";
trei.showVal( );
trei = doi - unu; // echivalent cu apelul explicit: operator-(doi, unu)
cout<< "\n Diferenta - (doi - unu) ";
trei.showVal( );
} 27
 Alegerea variantei de supraîncărcare – se ține cont de n-
aritatea operatorului, de considerente de protecţie şi de
modificare a valorii operanzilor:

 operatorii unari - de regulă, implementaţi cu metode membre


 operatorii binari ce modifică valoarea unui operand -
supraîncărcaţi cu metode membre
 dacă primul operand nu este obiect din clasa pentru care se
face supraîncărcarea - funcții operator globale (de regulă
friend)
 operatorii ce nu modifică operanzii, dar generează valori noi,
pot fi supraîncărcaţi cu funcţii globale:
 de tip friend, caz în care are acces la toate datele private ale clasei
 funcţie independentă care accesează datele prin metode de interfaţă

28
Expresie Operator Metoda membră Funcţie globală
@A + - * & ! ~ ++ -- A::operator@() operator@(A)
A@ ++ -- A::operator@(int) operator@(A,int)
+ - * / % ^ & | < >
A@B == != <= >= << >> A::operator@ (B) operator@(A,B)
&& || ,
= += -= *= /= %= ^=
A@B A::operator@ (B) -
&= |= <<= >>= []
A(B,C...) () A::operator( ) (B, C...) -
A->x -> A::operator->( ) -

 Obs: Operatorii increment/decrement ( ++/-- ) pot fi


prefixați și postfixați. În cazul postfixat se adaugă un
parametru redundant de tip int, pentru a distinge
semnăturile
29
CONVERSII DE TIP

 Compilatoarele C/C++ execută în mod automat conversii


pentru datele de tipuri diferite (regula conversiilor
implicite). Aceste conversii pot apărea la :

a) aplicarea unui operator între operanzi de tipuri diferite

b) parametrul efectiv al unei funcții este diferit ca tip de


parametrul formal corespunzător

c) tipul de retur din antetul unei funcții este diferit de cel


al expresiei din instrucțiunea return.

30
 Utilizatorul poate în anumite cazuri să impună realizarea
unor conversii cu operatorul cast. El poate fi utilizat în
C++ astfel:
(tip_cast) expresie
sau
tip_cast (expresie)

 Dacă se dorește realizarea unei operații între obiecte


dintr-o anumită clasă și operanzi de alte tipuri, trebuie
definite explicit regulile de conversie care se vor aplica

31
 De exemplu: să presupunem că dorim să putem aduna 2
obiecte de tip Complex, dar și un obiect de tip Complex
cu un număr întreg

class Complex {

public:
friend Complex operator+(Complex , Complex );
friend Complex operator+(Complex , int);
friend Complex operator+(int, Complex);
};

 Obs: s-au prevăzut alte două funcţii operator


(supraîncărcare de funcţii) pentru adunarea cu un întreg
32
Complex operator+(int a, Complex c)
{
Complex rez;
rez.re=a+c.re;
rez.im=c.im;
return rez;
}

Complex operator+(Complex c, int a)


{
Complex rez;
rez.re=c.re+a;
rez.im=c.im;
return rez;
}
33
Variante de conversii
1. Se poate utiliza un constructor pentru a converti obiectul
implicat la tipul clasei în care avem supraîncărcarea
(constructor de conversie de tip):

class Complex {

public:
Complex(int x) {
re =x; im = 0;
}
friend Complex operator+( Complex c1, Complex c2);
};
34
 Ca urmare este suficientă o singură funcţie operator şi
sunt posibile expresii care implică operanzi de tipul
Complex şi operanzi întregi :

Complex z(5,6), q, v;
q= z + 5; // echivalenta cu: q = operator+(z, Complex(5));
v=3+z; // echivalenta cu: v = operator+(Complex(3),z);

35
2. Altă variantă - utilizarea unei funcţii de conversie:
operator nume_tip ( ) {
// corp metoda
}
 O astfel de funcţie va converti un obiect al clasei către
nume_tip în cursul unei conversii explicite (cast).

36
class Point {
private:
int x, y;
public:
Point (int x, int y) { Point::x = x; Point::y = y; }
Point (int x) { Point::x = Point::y = x; } // constructor de conversie de tip

friend Point operator+ (Point p, Point q) ;


friend Point operator- (Point &p, Point &q);
void print (void) { cout<<"("<<x<<","<<y<<")"; }
};

Point operator+ (Point p, Point q) {


{
return Point(p.x + q.x, p.y + q.y);
}
Point operator- (Point &p, Point &q) {
return Point(p.x - q.x, p.y - q.y);
37
}
class Rectangle {
private:
Point topLeft;
Point botRight;
public:
Rectangle(int left, int top, int right, int bottom);
Rectangle(Point &p, Point &q) : topLeft(p), botRight(q) { }
Rectangle(Point &p) : topLeft(p), botRight(p) { }

operator Point( ) { // functie de conversie


return botRight - topLeft;
}
friend Rectangle operator+(Rectangle &r, Rectangle &t);
friend Rectangle operator-(Rectangle &r, Rectangle &t);

void print (void) {


topLeft.print();
botRight.print( );
38
}
};
Rectangle operator + (Rectangle &r, Rectangle &t) {
return Rectangle(r.topLeft + t.topLeft, r.botRight + t.botRight);
}
Rectangle operator - (Rectangle &r, Rectangle &t) {
return Rectangle(r.topLeft - t.topLeft, r.botRight - t.botRight);
}
Rectangle::Rectangle (int left, int top, int right, int bottom)
: topLeft(left,top), botRight(right,bottom)
{}

int main ( ){
Point p(10,10), q(20,20);
(p+q).print( );
cout<<'\n';
(p + 10).print( ); // echivalent cu operator+(p, Point(10))
cout<<'\n';
Rectangle r(10,10,20,30);
q =r + p; // echivalent cu operator+((Point)r, p) 39
q.print( );
}
Concluzie: utilizarea obiectelor duce la situații în care
sunt necesare conversii. Aceste conversii nu pot fi
aplicate automat, spre deosebire de cazul tipurilor
predefinite.

Utilizatorul poate defini următoarele tipuri de conversie:


a) din tip predefinit -> în tip abstract
b) din tip abstract -> în tip predefinit
c) din tip abstract -> în tip abstract

40
a) Conversia din tip predefinit -> în tip abstract
Se realizează în mod automat prin apelul unui
constructor de conversie adecvat. Constructorul va avea
ca și parametru tipul predefinit și eventual și alti
parametri care trebuie să fie toți impliciți.

b) Conversia din tip abstract -> în tip predefinit


Se realizează prin supraîncărcarea operatorului cast cu o
funcție membră de forma:
Nume_clasa:: operator nume_tip_predefinit ( ) {
// corp metoda
}
Metoda nu are nici un parametru, asta înseamnă că
operatorul cast este un operator unar.
Ea se apelează pentru un obiect de tip Nume_clasa și
returnează o valoare de tip nume_tip_predefinit.
41
Se utilizează asemănător cu tipurile predefinite :
(nume_tip_predefinit) obiect
sau
nume_tip_predefinit (obiect)

c) Conversia din tip abstract -> în tip abstract


 Această conversie se poate realiza cu ajutorul
constructorilor de conversie, cât și prin supraîncărcarea
operatorului cast.
 Dacă se folosește un constructor de conversie:
presupunem că avem tip1 și tip2 două tipuri abstracte,
pentru a converti un obiect de tip1 în obiect tip2, se
definește un constructor pentru clasa tip2, care să aibă
ca și parametru un obiect de tip1 de forma:
tip2(tip1 obiect);
42
Supraîncărcarea operatorului de atribuire (=)
(asignare)

 Pentru atribuirile între obiecte de acelaşi tip, se utilizează


o variantă supraîncărcată implicită a operatorului de
atribuire

 Această variantă asigură copierea datelor membru cu


membru

 În cazul în care constructorii realizează alocări dinamice,


aceste variante implicite nu sunt satisfăcătoare şi este
necesară furnizarea unei variante explicite de
supraîncărcare
43
Forma generală a metodei de supraîncărcare a operatorului
de asignare este :

Tip& Tip::operator=(const Tip &sursa)


{
if(this != &sursa)
{
// copiere date aferente obiectului din membrul drept
}
return *this;
}

 Funcţia operator trebuie să fie o metodă membră (nu poate


fi funcţie externă)
44
class Matrix
{
private:
int rows;
int cols;
int *elems;
public:
Matrix (int rows, int cols);
~Matrix (void) {
delete [ ] elems;
}
Matrix (const Matrix&); //copy constructor
Matrix& operator = (const Matrix &m);
}; 45
Matrix::Matrix (const int r, const int c) : rows(r), cols(c)
{ // rows=r; cols=c;
elems = new int[rows * cols];
}

Matrix::Matrix (const Matrix &m)


{
int n;
rows = m.rows;
cols = m.cols;
n = m.rows * m.cols;
elems = new int[n];
for (int i = 0; i < n; ++i)
elems[i] = m.elems[i];
} 46
Matrix& Matrix::operator = (const Matrix &m)
{
if (this != &m)
{
if (rows == m.rows && cols == m.cols)
{
int n = rows * cols;
for (int i = 0; i < n; ++i)
elems[i] = m.elems[i];
}
}
return *this;
}
47
 Trebuie făcută distincţia între atribuire (asignare) şi
iniţializare, pentru ambele operaţii folosindu-se simbolul =
 când simbolul = apare într-o declaraţie cu iniţializare,
avem iniţializare, care se face cu ajutorul
constructorului de copiere:

Tip ob2(ob1); // ob2 nu exista inaintea acestei
// linii de cod si va fi initializat cu ob1
Tip ob2=ob1;

 când simbolul = apare într-o expresie în care ambele


obiecte sunt deja create, avem de-a face cu o atribuire
pentru care se foloseşte varianta supraîncărcată a
operatorului de atribuire:
Tip ob1, ob2; // definire obiecte

ob2 = ob1; // asignare
48
class String {
char *p;
int size;
public:
String(char *str);
String(void);
String(const String &obj); // constructor de copiere
~String(void) {
delete [ ] p;
}

// supraîncărcarea operatorului de atribuire


String operator=(String &obj);
String operator=(char *s);
49
// supraincarcarea operatorului + (concatenare)
// String operator+(String &obj); // cu metoda membra
friend String operator+(String &obj1, String &obj2);
String operator+(char *s);
friend String operator+(char *s, String &obj);

// supraincarcarea operatorului - (substractie)


// String operator-(String &obj); // cu metoda membra
friend String operator-(String &obj1, String & obj2);
String operator-(char *s);

50
// supraincarcarea operatorilor relationali: compara obiectul curent cu un alt obiect

int operator==(String &obj)


{ return !strcmp(p, obj.p); }
int operator!=(String &obj)
{ return strcmp(p, obj.p); }
int operator<(String &obj)
{ return strcmp(p, obj.p) < 0; }
int operator>(String &obj)
{ return strcmp(p, obj.p) > 0; }
int operator<=(String &obj)
{ return strcmp(p, obj.p) <= 0; }
int operator>=(String &obj)
{ return strcmp(p, obj.p) >= 0; }
51
// supraincarcarea operatorilor relationali: compara obiectul curent cu
un sir

int operator==(char *s)


{ return !strcmp(p, s); }
int operator!=(char *s)
{ return strcmp(p, s); }
int operator<(char *s)
{ return strcmp(p, s) < 0; }
int operator>(char *s)
{ return strcmp(p, s) > 0; }
int operator<=(char *s)
{ return strcmp(p, s) <= 0; }
int operator>=(char *s)
{ return strcmp(p, s) >= 0; }

52
int strsize(void)
{ return strlen(p); }
char* makestr(char *s)
{ return strcpy(p, s); }
operator char *(void)
{ return p; }
}; // clasa String

String& String::operator = (String &x){


if( this == &x)
return *this;
if( p != NULL)
delete [] p;
p = new char[strlen(x.p)+1];
strcpy(p,x.p);
return *this; 53
}
String operator + (String &a,String &b){
char buf[200];
strcpy(buf,a.p);
strcat(buf,b.p);
return String(buf);
}

String operator - (String &a, String &b){


char *pp;
pp = strstr(a.p, b.p); // adresa unde începe sirul ob.b in sirul ob.a
if(pp == NULL) // sirul nu se gaseste in ob.a
return String(a.p);
else {
char buf[200];
strncpy(buf, a.p, pp-a.p);
strcpy(buf+(pp-a.p),pp+strlen(b.p));
return String(buf);
} 54

}
Supraîncărcarea operatorului de indexare [ ]

 Supraîncărcarea acestui operator se face pentru acele


tipuri de date ce au un comportament similar cu tablourile
 Un exemplu în acest sens este tabloul asociativ, în care
indecşii pot fi instanţe ale oricărui tip ordonat
 Prin supraîncărcarea acestui operator se urmăreşte
obţinerea unui comportament de felul următor:
a[b]  a.operator[ ](b)
Dacă se face supraîncărcarea operatorului de indexare
pentru un anumit tip, atunci tablourile de acest tip nu mai
pot fi iniţializate prin construcţii de forma:
Tip nume_tablou[ ] = {val1, val2,…, valn};
ci numai prin intermediul constructorilor
Supraîncărcarea acestui operator se poate realiza numai cu
metode membre. 55
 Exemplul 1: Dicţionar
struct assoc {
char *cuvant;
char *definitie;
};

class Dictionar {
assoc *dict;
int dim; // dimensiunea dictionarului
int next; // urmatoarea pozitie libera in dictionar

public:
Dictionar(int size) {
dim = size;
next = -1;
dict = new assoc[size];
}

int addWord(char *w, char *def); 56


char *operator[ ](const char *);
};
int Dictionar::addWord(char *word, char *def)
{
if(next <dim) {
next++;
dict[next].cuvant = new char[strlen(word)+1];
strcpy(dict[next].cuvant, word);
dict[next].definitie = new char[strlen(def)+1];
strcpy(dict[next].definitie, def);
return 0;
}
return 1;
}

57
char * Dictionar::operator [ ](const char *pCuv) {
assoc *pAssoc;
for(pAssoc = &dict[next-1]; dict <= pAssoc; pAssoc--)
if(strcmp(pCuv, pAssoc->cuvant) == 0)
return pAssoc->definitie;
return 0;
}

int main( ) {
int size = 10;
Dictionar dict(size);
dict.addWord("Masina", "Definitia 1");
dict.addWord("Copac", "Definitia 2");
dict.addWord("Animal", "Definitia 3");
dict.addWord("Student", "Definitia 4");
cout << dict["Animal"]<<endl;
}
58
Exemplul 2: clasa Analize

class Analize;
class Pers
{
char nume[20];
double greutate;
int varsta;
friend Analize;
public:
void tip();
};// Pers

59
class Analize {
Pers *sir;
int n;
public:
Analize()
{
n=5;
sir= new Pers[5];
}
Analize(int nr)
{
n=nr;
sir= new Pers[n];
}
//Supraincarcare []
Pers* operator[](char *);
Pers* operator[](double);
Pers* operator[](int);
void introdu(); // functie pentru citire date persoane– trebuie definita 60

};//Analize
//Indexare dupa nume
Pers* Analize:: operator[](char *nume) {
for(int i=0;i<n;i++)
if(strcmp(sir[i].nume,nume)==0)return &sir[i];
return NULL;
}//op[]nume

//Indexare dupa greutate


Pers* Analize:: operator[](double gr) {
for(int i=0;i<n;i++)
if(sir[i].greutate==gr)return &sir[i];
return NULL;
}//op[]greutate

//Indexare dupa index


Pers* Analize:: operator[](int index) {
if((index >=1) && (index <=n))return &sir[index-1];
else {cout << "\nIndex gresit";return NULL;}
}//op[]index 61
int main()
{
int nr=10;
double g;
char *n;
int i;
// g, n si i vor fi initializate

Analize t(nr); //creare obiect de tip Analize


t.introdu(); // citirea datelor
t[g]->tip(); // afisare persoana cu greutatea g
t[n]->tip(); // afisare persoana cu numele n
t[i]->tip(); // afisare persoana cu indicele i in tablou
}

62
Exemplul 3: BitVec
#include <iostream>
using namespace std;
typedef unsigned char uchar;
class BitVec {
uchar *vec;
short bytes;
public:
BitVec (const short dim);
BitVec (const char* bits);
BitVec (const BitVec&);
~BitVec (void) { delete [ ] vec; }
BitVec& operator = (const BitVec&);
int operator [ ] (const short idx);
void print (void); 63
};//BitVec class
BitVec::BitVec (const char *bits){ // 1011011101111
int len = strlen(bits);
bytes = len / 8 + (len % 8 == 0 ? 0 : 1); Ex: len=13 -> bytes=2
vec = new uchar[bytes]; vec[1] | (1<< 4) ->00010000
vec[1] | (1<< 2) ->00010000 | 000001
for (int i = 0; i < bytes; ++i)
00010100
vec[i] = 0; vec[1] | (1<< 1) -> 00010100 | 000000
for (int i = len - 1; i >= 0; --i) 00010110
if (*bits++ == '1') vec[i/8] |= (1 << (i%8)); }
BitVec::BitVec (const short dim){
bytes = dim / 8 + (dim % 8 == 0 ? 0 : 1);
vec = new uchar[bytes];
for (int i = 0; i < bytes; ++i)
vec[i] = 0; }
BitVec::BitVec (const BitVec &v){ bytes = v.bytes;
vec = new uchar[bytes];
64
for (int i = 0; i < bytes; ++i)
vec[i] = v.vec[i]; }
BitVec& BitVec::operator= (const BitVec &v){
int i;
if (this != &v) {
for (i = 0; i < (v.bytes < bytes ? v.bytes : bytes); ++i)
vec[i] = v.vec[i];
for (; i < bytes; ++i)
vec[i] = 0; Exemplu: idx=18
} vec[2] & (1 << 2) ? 1 : 0
return *this; -> se returneaza al 3-lea bit (de
la dreapta) din al 3-lea octet
}
inline int BitVec::operator [ ] (const short idx){
return vec[idx/8] & (1 << idx%8) ? 1 : 0; }
void BitVec::print(void){
cout <<"\nOctetii sunt in hexa: "<<endl;
for(int i=0; i<bytes; i++)
65
cout <<"\nOctet: "<< (i+1)<< hex <<": "<< (int)vec[i] << endl; }
int main (void){
BitVec v("1011011101111");
v.print( );
cout << endl << "\nBitii sunt: " << dec << endl;
for(int i=0; i< 16; i++)
cout << "Bit " << (i+1) << ": " << v[i] << endl;
BitVec vv(16);
cout<<"\n Noul vector initial este:";
vv.print();
vv=v;
cout<<"\n Noul vector dupa asignare este:";
vv.print();
cout << endl << "\nBitii sunt: " << dec << endl;
for(int i=0; i< 16; i++)
cout << "Bit " << (i+1) << ": " << vv[i] << endl;
66
}//main
Supraîncărcarea operatorului apel de funcţie ( ) -
iteratori

 Supraîncărcarea acestui operator se foloseşte pentru


obţinerea unui iterator:
 acesta permite parcurgerea secvenţială a unei colecţii
 De obicei, se creează o clasă iterator asociată cu clasa
colecţiei pe care iteratorul o va parcurge
 Prin supraîncărcare se urmăreşte obţinerea unui
comportament de felul următor:
a(b,…)  a.operator( )(b,…);
Supraîncărcarea acestui operator se poate realiza numai cu
metode membre.
67
 Matrice
class Matrix {
private:
const short rows;
const short cols;
int *elems;
public:
Matrix(const short rows, const short cols);
Matrix(const Matrix&);
~Matrix (void) {delete [ ] elems;}
int& operator( ) (const short row, const short col);
Matrix& operator=(const Matrix&);

const short getRows (void) { return rows; }


const short getCols (void) { return cols; }
}; 68
Matrix::Matrix (const short r, const short c) : rows(r), cols(c)
{
if (rows > 0 && cols > 0)
elems = new int[rows * cols];
}

Matrix::Matrix (const Matrix &m) : rows(m.rows), cols(m.cols)


{
int n = rows * cols;
if (rows > 0 && cols > 0)
elems = new int[n];
for (register i = 0; i < n; i++)
elems[i] = m.elems[i];
}

69
int& Matrix::operator( ) (const short r, const short c) {
if((r >= 0 && r< rows)&&(c >= 0 && c < cols))
return elems[r *cols + c];
else{
cout<<"\nIndici eronati! Oprire program! \n";
exit(0);
}
}

Matrix& Matrix::operator= (const Matrix &m){


if (this != &m) {
if (rows == m.rows && cols == m.cols) {
int n = rows * cols;
for (register i = 0; i < n; i++)
elems[i] = m.elems[i];
}
}
return *this; 70
}
int main (void)
{
int i, j;
Matrix m(2,3), p(2,3);
m(0,0) = 10; m(0,1) = 20; m(0,2) = 30;
m(1,0) = 15; m(1,1) = 25; m(1,2) = 35;

for(i=0; i<2; i++) {


cout<<endl;
for(j=0; j<3; j++)
cout<<m(i, j)<<"\t";
}

Matrix n=m;
cout<<endl;
for(i=0; i<2; i++) {
cout<<endl;
for(j=0; j<3; j++)
cout<<n(i, j)<<"\t";
71
}
}
SUPRAÎNCĂRCAREA OPERATORILOR DE INCREMENTARE/
DECREMENTARE (++/ --)
 Operatori unari, pot fi prefixati și postfixati.
 În cazul postfixat se adaugă un parametru redundant de tip int,
pentru a distinge semnăturile
 Operațiile se fac în obiectul curent, care va fi returnat de funcția
operator

Operator prefixat supraîncărcat în clasa A cu metode membre


A::operator++(), respectiv A::operator--()
Operator postfixat supraîncărcat în clasa A cu metode membre
A::operator++(int), respectiv A::operator--(int)

Operator prefixat supraîncărcat cu funcție friend


operator++(A), respectiv operator--(A)
Operator postfixat supraîncărcat cu funcție friend 72
operator++(A, int), respectiv operator--(A, int)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Time{
private:
int hh; // ora
int mm; // minut
public:
Time(int m) {
if (m >= 0) {
hh = m / 60; // nr ore
mm = m % 60; // nr minute
}
else {
hh = mm = 0;
}
}
73
Time(int h = 0, int m = 0) {
if (((h >= 0) && (h < 24)) && ((m >= 0) && (m < 60))){
hh = h;
mm = m;
}
else { hh = mm = 0; }
} //Time

int getH(){
return hh;
}

void setH(int h) {
if ((h >= 0) && (h < 24))
hh = h;
else
hh = 0; }

int getM(){ 74
return mm;
}
void setM(int m) {
if ((m >= 0) && (m < 60))
mm = m;
else
mm = 0;
}

void show() {
cout << hh << ":" << mm << endl;
}
// supraincarcare operator de incrementare prefixata
Time& operator++() {
mm++;
if (mm == 60) {
hh++;
if (hh == 24)
hh = 0;
mm = 0;
}
75
return *this;
}
// supraincarcare operator de incrementare postfixata
Time operator++(int) {
Time temp = *this;
++(*this);
return temp;
}
// supraincarcare operator de decrementare prefixata
Time& operator--() {
if (mm == 0) {
hh--;
if (hh < 0) hh = 23;
mm = 59;
}
else
mm--;
return *this;
76
}
// supraincarcare operator de decrementare postfixata
Time operator--(int) {
Time temp = *this;
--(*this);
return temp;
}

// supraincarcare operator de atribuire compusa +=


Time& operator+=(int min) {
int h1, m1;
h1 = min / 60;
m1 = min % 60;
hh = (hh + h1 + ((mm+m1) / 60)) % 24;
mm = (mm + m1) % 60;
return *this;
}

77
// supraincarcare operator de atribuire compusa -=
Time& operator-=(int min) {
int h1, m1;
h1 = min / 60;
m1 = min % 60;
hh = hh - h1;
if (mm < m1) hh--;
if (hh < 0) hh = 24 + hh;
mm = mm - m1;
if (mm < 0) mm = 60 + mm;
return *this;
}
// supraincarcare operator de adunare +
Time operator+(Time tm) {
Time rez;
int nm = mm + tm.mm;
rez.hh = hh + tm.hh + nm / 60;
if (rez.hh > 23) rez.hh = rez.hh - 24;
rez.mm = nm % 60;
78
return rez;
}
// supraincarcare operator de scadere-
Time operator-(Time tm){
Time rez;
if (mm < tm.mm)
{
rez.hh = hh - tm.hh - 1;
rez.mm = 60 - (tm.mm - mm);
}
else
{
rez.hh = hh - tm.hh;
rez.mm = mm - tm.mm;
}
if (rez.hh < 0)
rez.hh = 24 + rez.hh;
return rez;
}
};
79
int main(){
Time tm1(1, 29);
cout << "Time 1: ";
tm1.show();
cout << endl;
Time tm2;
tm2 = --tm1;
cout << "Time 1 (tm2 = --tm1): ";
tm1.show();
cout << "Time 2 (tm2 = --tm1): ";
tm2.show();
cout << endl;
tm1 = ++tm2;
cout << "Time 1: (tm1 = ++tm2)";
tm1.show();
cout << "Time 2: (tm1 = ++tm2)";
tm2.show();

80
cout << endl;
tm1 = tm2++;
cout << "Time 1 (tm1 = tm2++): ";
tm1.show();
cout << "Time 2 (tm1 = tm2++): ";
tm2.show();
tm2--;
cout << "Time 2 (tm2--): ";
tm2.show();

cout << endl;


Time tm3(22, 15);
cout << "Time 3: ";
tm3.show();

81
cout << endl;
tm3 += 75;
cout << "Time 3 (+=75): ";
tm3.show();
tm3 -= 86;
cout << "Time 3 (-=86): ";
tm3.show();

Time tm4(23, 18);


cout << endl;
cout << "Time 4: ";
tm4.show();
cout << "Time 3 + Time 4 : ";
(tm3+tm4).show();
cout << "Time 3 - Time 4 : ";
(tm3 - tm4).show();

}
82
Supraîncărcarea operatorilor new şi delete

 Scop: de a efectua anumite operaţii speciale de


alocare/eliberare de memorie în momentul creării sau
distrugerii unui obiect din clasa în care aceştia au fost
supraîncărcaţi

 Operatorii new şi delete pot fi supraîncărcaţi numai cu


metode membre statice (nu se precizează în mod explicit)

83
 Sintaxa generală de supraîncărcare locală (sau utilizator) a
acestor operatori cu metode statice:

void *operator new(size_t dim)


{
… // operatii de alocare de memorie
return pointer_la_noua_zona_de_memorie;
}

void operator delete(void *p, [size_t dim])


{
… // elibereaza zona pointata de p
}
84
 La aplicarea acestor operatori nu se indică nicio valoare
pentru parametrul formal dim, compilatorul va calcula
automat dimensiunea obiectului pentru care se rezervă
zona de memorie, dimensiune ce se va atribui parametrului
dim
 Apelul se va face prin:

new Nume_clasa ;
sau:
new Nume_clasa(par1, par2, …, parn) ;

85
 Operatorii new şi delete au însă o particularitate:
 au o supraîncărcare globală (standard sau predefinită)
oferită de limbaj şi pentru tipurile abstracte
 pot avea o supraîncărcare locală unei clase

 Simultan poate fi folosită şi supraîncărcarea globală, printr-


o construcţie de forma :
:: new Nume_clasa ;

86
 Supraîncărcarea globală permite alocarea tablourilor de
obiecte, pe când cea locală nu
 În cazul în care se doreşte alocarea/dealocarea specială
de memorie pentru tablouri, se folosesc următoarele
sintaxe:
void *operator new[ ](size_t dim)
{
… // operatii de alocare de memorie
return pointer_la_noua_zona_de_memorie;
}

void operator delete[ ](void *p)


{
… // elibereaza zona de memorie pointata de p
87
}
Exemplul 1:
class Pozitie{
int x;
int y;
public:
Pozitie(){}
Pozitie(int oriz, int vert){
x = oriz;
y = vert;
}
void arata(){
printf("\nPozitia in plan are coordonatele: %d, %d", x, y);
}
void *operator new(size_t marime);
void operator delete(void *p);
};
void *Pozitie :: operator new(size_t marime){
printf("\n Supraincarc local operatorul new.");
return malloc(marime);
88
}
void Pozitie :: operator delete(void *p){
printf("\n Supraincarc local operatorul delete.");
free(p);
}
int main(void){
Pozitie *p1, *p2;
p1 = new Pozitie(100, 200);
if(!p1){
printf("\n Nu am putut aloca memorie pt. p1 ");
exit(0);
}
p2 = new Pozitie(10, 20);
if(!p2){
printf("\n Nu am putut aloca memorie pt. p2 ");
exit(1);
}
p1->arata();
p2->arata();
delete(p1);
89
delete(p2);
}
EXEMPLUL 2: NEW AND DELETE
#include <iostream>
#include <cstdlib>
using namespace std;

class Point {
double x, y;
public:
Point() { x = y = 0.0; }
Point(double x, double y) {
this->x = x;
this->y = y;
}
int getX() { return x; }
int getY() { return y; }
void setX(int x) { this->x = x; }
void setY(int y) { this->y = y; }
void * operator new(size_t size);
void operator delete(void * ptr);
void * operator new[](size_t size);
void operator delete[](void * ptr); 90
};
void* Point::operator new(size_t size){
void * ptr= malloc(size);
cout << “Utilizare operator new supraincarcat."<<endl;
return ptr;
}
void Point::operator delete(void * ptr){
cout << “Eliberare memorie cu operatorul delete supraincarcat" << endl;
free(ptr);
}

void * Point::operator new[](size_t size){


cout << “Utilizare operator new[] supraincarcat" << endl;
void *ptr = malloc(size);
return ptr;
}

void Point::operator delete[](void * ptr){


cout << “Eliberare memorie cu operator delete[] supraincarcat" << endl;
free(ptr);
}
91
int main(){
Point * p1; // pointer la clasa Point
if (p1 = new Point[5]) {
cout << “Obiecte Point:" << endl;
for (int i = 0; i < 5; i++)
cout << p1[i].getX() << " " << p1[i].getY() << endl;
}
else{
cout << "Eroare alocare memorie! " << endl; return 1;
}
delete[] p1;
cout << "-----------------------------------"<<endl;
Point *p2 = new Point(5, 7);
if (p2)
cout << p2->getX() << " " << p2->getY() << endl;
else
cout << "Eroare creare obiect";
if (p2)
delete p2;

92
// utilizarea functiei operator globale operator new[]() pentru tipul float

cout << "-----------------------------------"<<endl;

float *p3 = new float[10]; // apel functie apel ::operator new

delete[] p3; // ::operator delete


cin.get();
return 0;
} // main

93
 Intrebări
- Ce operatori nu pot fi supraincarcati in C++?
- Cum pot fi supraincarcati operatorii in C++?
- Cum se face supraincarcarea cu metode membre?
- Cum se face supraincarcarea cu functii friend?
- Ce operatori pot fi supraincarcati doar cu metode membre?
- Metoda de supraincarcare a operatorului = se mosteneste intr-
un proces de derivare a claselor?
- Cand este necesara supraincarcarea operatorului =?
- Cum se face supraincarcarea operatorului =?
- Cum se realizeaza supraincarcarea operatorilor [] si ()?
- Cum se realizeaza supraincarcarea operatorilor new si delete?
Variantele supraincarcate pot fi folosite pentru alocarea
dinamica a tablourilor?
- Cum se face supraincarcarea operatorilor ++ si – prefixati si
94
postfixati?

S-ar putea să vă placă și