Documente Academic
Documente Profesional
Documente Cultură
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.
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):
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
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) ;
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:
prioritatea
asociativitatea
tipul clasei
12
Operatorii ->, =, [ ] şi ( ) și cei compuși pot fi
supraîncărcaţi numai cu metode membre
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";
}
Forma generală:
friend tip_returnat operator#(lista_argumente) ;
void showVal(void) {
cout << "Date membre: "<< “re = " << re;
cout << ", im = " << im << "\n";
}
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->( ) -
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)
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);
};
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
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.
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.
50
// supraincarcarea operatorilor relationali: compara obiectul curent cu un alt obiect
52
int strsize(void)
{ return strlen(p); }
char* makestr(char *s)
{ return strcpy(p, s); }
operator char *(void)
{ return p; }
}; // clasa String
}
Supraîncărcarea operatorului de indexare [ ]
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];
}
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
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
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 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
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;
}
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();
81
cout << endl;
tm3 += 75;
cout << "Time 3 (+=75): ";
tm3.show();
tm3 -= 86;
cout << "Time 3 (-=86): ";
tm3.show();
}
82
Supraîncărcarea operatorilor new şi delete
83
Sintaxa generală de supraîncărcare locală (sau utilizator) a
acestor operatori cu metode statice:
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
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;
}
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);
}
92
// utilizarea functiei operator globale operator new[]() pentru tipul float
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?