Documente Academic
Documente Profesional
Documente Cultură
Supraîncărcarea operatorilor.
1. Supraîncărcare.
2. Funcţii prieten.
3. Modalităţi de supraîncărcare a operatorilor.
4. Operatori redefiniţi ca funcţii prieten.
5. Operatori redefiniţi ca funcţii membri.
6. Supraîncărcarea operatorului de atribuire.
7. Supraîncărcarea operatorului de indexare.
8. Supraîncărcarea operatorilor new şi delete.
9. Supraîncărcarea operatorului apel de funcţie.
10. Supraîncărcarea operatorilor de conversie.
11. Supraîncărcarea operatorilor << şi >>.
12. Probleme.
Operatorii sunt notaţii concise, infixate, pentru operaţii matematice uzuale. Limbajul C++, ca
orice limbaj de programare asigură un set de operatori pentru tipurile primitive. În plus, faţă de
limbajul C, C++ oferă posibilitatea asocierii operatorilor existenţi cu tipurile definite de utilizator .
Astfel, prezintă interes extinderea operatorilor în aritmetică complexă, algebra matricială, în lucrul cu
şiruri de caractere, etc. Un operator poate fi privit ca o funcţie, în care termenii sunt argumentele
funcţiei (în lipsa operatorului +, expresia a+b s-ar calcula apelând funcţia aduna(a,b)).
Limbajul C++ introduce următorii operatori noi: new şi delete- pentru gestiunea
memoriei dinamice, operatorul de rezoluţie (::) şi operatorii de acces la membri: .* şi ->*.
1. Supraîncărcare.
Prin supraîncărcarea unui operator, acesta este utilizat în contexte diferite, avînd aceeaşi
semnificaţie sau o semnificaţie diferită. Astfel operatorul + este supraîncărcat pentru adunarea
întregilor şi a realilor; operatorul << este supraîncărcat cu semnificaţia de deplasare la stînga, dacă
termenii sunt întregi sau inserare în flux, dacă un termen este un flux de date, iar celălalt un obiect.
Programatorul poate asocia noi semnificaţii operatorilor existenţi prin supraîncărcarea
operatorilor. Vom evita folosirea termenului redefinire, căruia îi vom da o semnificaţie diferită, în
contextul moştenirii.
Anumiţi operatori pot fi utilizaţi cu orice tip de termeni (sunt deja supraîncărcaţi global de
către compilator). Aceştia sunt: new, delete, sizeof, ::, =, &, ->*, .*, ., ->.
Pot fi supraîncărcaţi următorii operatori:
+ - * / % ^ & | ~ ! = < >
+= -= *= /= %= ^= &= |= >>= <<= == != <= >=
&& || ++ -- ->* , -> << >> [] ()
new new[] delete delete[]
Nu pot fi supraîncărcaţi operatorii: ::, ., .*, ?:, sizeof.
Setul de operatori ai limbajul C++ nu poate fi extins prin asocierea de semnificaţii noi unor
caractere, care nu sunt operatori de exemplu nu putem defini operatorul **).
Prin supraîncărcarea unui operator nu i se poate modifica aritatea (astfel operatorul ! este
unar şi poate fi redefinit numai ca operator unar. De asemeni asociativitatea şi precedenţa operatorului
se menţin.
La supraîncărcarea unui operator nu se pot specifica argumente cu valori implicite.
Operatorii supraîncărcaţi într-o clasă sunt moşteniţi în clasele derivate (excepţie face
operatorul de atribuire =).
Semnătura unei funcţii în care se supraîncarcă un operator este:
tip_rezultat operator#(listă_argumente);
1
Valeriu Iorga Programare în C / C++
2. Funcţii prieten.
În afara funcţiilor membre, datele private şi cele protejate mai pot fi accesate de funcţiile
prieten (friend) ale clasei. O funcţie este considerată prietenă al unei clase, dacă declararea funcţiei
precedate de specificatorul friend, apare în declararea clasei,. Definiţia funcţiei prieten se face
global, în afara clasei.
Dacă o funcţie are ca parametri obiecte aparţinând la două clase diferite, atunci ea poate fi
declarată ca funcţie membru a unei clase şi prieten al celeilalte clase, sau ca funcţie prietenă ambelor
clase.
De exemplu fie o funcţie de înmulţire a unei matrice cu un vector. Parametrii funcţiei vor fi
două obiecte – o matrice şi un vector. Clasele respective Mat şi Vec pot declara fiecare, funcţia
MatVec() ca prietenă:
class Mat{
int n;
double **m;
public:
Mat();
Mat(Mat&);
. . .
friend Vec MatVec(Mat&, Vec&);
};
class Vec{
int n;
double *v;
public:
Vec();
Vec(Vec&);
. . .
friend Vec MatVec(Mat&, Vec&);
};
De asemenea funcţiile prieten se utilizează în contextual supraîncărcării operatorilor.
Declararea unei funcţii prieten poate fi făcută în orice parte a clasei (publică, privată sau
protejată).
O funcţie prietenă a unei clase poate accesa toţi membrii clasei. Dacă o clasă este prietenă cu
altă clasă, membrii ei pot accesa membrii celeilalte clase.
2
Valeriu Iorga Programare în C / C++
Operatorii care produc o nouă valoare din valorile argumentelor (de exemplu operatorul+) se
definesc în afara clasei.
Dacă nu sunt necesare conversii de tip, alegerea între funcţie membru şi funcţie prieten
rămâne la latitudinea programatorului.
O funcţie nemembră foloseşte numai argumente explicite, în timp ce o funcţie membră
foloseşte argumentul implicit this.
Argumentele clase, transmise prin valoare se copiază în stivă neeconomic. Se preferă în acest
caz argumentele referinţe constante.
Dacă valoarea întoarsă de funcţie este o referinţă, atunci aceasta nu poate fi o variabilă
automatică (locală funcţiei). Ea nu poate fi nici o variabilă statică locală, deoarece operatorul poate fi
folosit de mai multe ori într-o expresie. Valoarea de întoarcere trebuie să fie alocată în memoria liberă
(heap) sau să fie preluată dintr-un buffer de obiecte statice.
Întoarcerea unei referinţe la obiectul modificat se realizează prin return *this.
Se caută minimizarea numărului de funcţii care au acces la reprezentarea internă a unei clase,
prevăzându-se funcţii de acces.
În mod obligatoriu sunt funcţii membre: constructorii, destructorii, funcţiile virtuale, etc.
4. Operatori supraîncărcaţi ca funcţii prieten.
Operatorii folosiţi în mod uzual pot fi unari sau binari. Utilizarea unui operator binar sub
forma a#b este interpretată ca operator#(a,b)
Aşadar, un operator binar va fi reprezentat printr-o funcţie nemembră cu două argumente, iar
un operator unar, printr-o funcţie nemembră cu un singur argument.
Argumentele se iau clase sau referinţe constante la clase (pentru o preluare economică,
asigurând protecţia datelor)
Vom exemplifica pentru clasa Cplx:
class Cplx{
double re, im;
public:
Cplx(double x=0, double y=0);
Cplx(const Cplx& z);
//operatori binari
friend Cplx operator+(const Cplx& s, const Cplx& d);
friend Cplx operator-(const Cplx& s, const Cplx& d);
friend Cplx operator*(const Cplx& s, const Cplx& d);
friend Cplx operator/(const Cplx& s, const Cplx& d);
//operatori de comparatie
friend int operator==(const Cplx& s, const Cplx& d);
friend int operator!=(const Cplx& s, const Cplx& d);
//operatori unari
friend Cplx operator-(const Cplx& z);
friend Cplx operator!(const Cplx& z); //conjugat
friend Cplx& operator++(Cplx& z); //prefix
friend Cplx operator—-(Cplx& z,int); //postfix
};
//definitii operatori în afara domeniului clasei
Cplx operator+(const Cplx& s, const Cplx& d){
return Cplx(s.re+d.re,s.im+d.im);
};
Cplx operator-(const Cplx& s, const Cplx& d){
return Cplx(s.re-d.re,s.im-d.im);
};
3
Valeriu Iorga Programare în C / C++
4
Valeriu Iorga Programare în C / C++
5
Valeriu Iorga Programare în C / C++
};
Cplx& Cplx::operator-(){
re=-re;
im=-im;
return *this;
};
Cplx& Cplx::operator!(){
im=-im;
return *this;
};
Cplx& Cplx::operator++(){
re++;
return *this;
};
Cplx Cplx::operator++(int){
Cplx t(re,im);
re++;
return t;
};
6
Valeriu Iorga Programare în C / C++
n=d.n; //copiere
s=new char[n];
strncpy(s, d.s, n);
};
return *this; //intoarce obiectul modificat
};
//ctor de initializare (cu un sir tip C)
String::String& operator=(const char* p){
if(s)
delete [] s;
n=strlen(p);
s=new char[n];
strncpy(s, p, n);
return *this;
};
7. Supraîncărcarea operatorului de indexare.
Operatorul de indexare este un operator binar, având ca prim termen obiectul care se
indexează, iar ca al doilea termen indicele: obiect [ indice ] şi este interpretat ca:
obiect.operator[ ](indice) .
Funcţia operator de indexare trebuie să fie funcţie membră nestatică.
Primul termen, transmis implicit prin this este obiectul asupra căruia se execută indexarea.
Argumentul funcţiei reprezintă indicele, care poate fi de orice tip, spre deosebire de indicii
predefiniţi şi este al doilea termen din operator.
Valoarea întoarsă de funcţia operator este o referinţă la elementul indexat din obiect.
Exemplificăm cu operatorul de indexare pentru clasa String:
class String{
int n;
char* s;
public:
. . .
char& operator[](int);
};
char& String:: operator[](int i){
if(i>=0 && i<n)
return s[i];
else
return 0;
};
8. Supraîncărcarea operatorilor new şi delete.
Operatorii new şi delete pot fi supraîncărcaţi ca funcţii operatori membre statice.
Funcţia operator new are semnătura: void* operator new(size_t); Operatorul
redefinit apelează constructorul clasei după alocarea dinamică pe care o realizează.
Funcţia operator delete are semnătura: void operator delete(void*); unde
parametrul este un pointer la obiectul eliminat. Operatorul redefinit delete apelează întotdeauna
destructorul clasei înainte de a elibera memoria alocată dinamic.
Dacă în clasa C se supraîncarcă operatorul new, atunci versiunea furnizată de sistem se obţine
prin C *p = ::new C();
Exemplu1:Supraîncărcaţi operatorul new, astfel încât să iniţializeze memoria alocată la 0.
void* C::operator new(size_t dim){
void *p = new char[dim];
return memset(p, 0, sizeof(C));
}
7
Valeriu Iorga Programare în C / C++
8
Valeriu Iorga Programare în C / C++
private:
long x, y;
};
long Fibo::operator ()(int n){
for(int i=2; i<=n; i++){
long z=x+y;
x=y;
y=z;
};
return x;
};
10. Supraîncărcarea operatorilor de conversie.
În C++ la evaluarea unei expresii se efectuează conversii implicite pentru tipurile predefinite.
Deoarece nu există conversii implicite de la o clasă la un tip predefinit, programatorul îşi poate defini
conversii explicite prin supraîncărcarea unui operator de conversie al clasei. Astfel pentru clasa C,
funcţia membru nestatică de conversie C::operator T() unde T este un nume de tip primitiv
realizează conversia de la tipul C la tipul T. Aceasta nu specifică nici un tip de rezultat întors, deoarece
se returnează implicit valoarea obiectului convertită la tipul pentru care este definită conversia şi nu are
parametri.
Funcţia operator primeşte ca prim parametru implicit adresa obiectului şi întoarce valoarea
convertită de tipul numelui funcţiei operator de conversie.
Apelul explicit al funcţiei de conversie de la un obiect din clasa C la tipul primitiv T se
specifică prin T(obiect) sau (T) obiect.
Pentru conversia unui obiect între două clase C1 şi C2,C1->C2 se defineşte funcţia membru
operator de conversie C1::operator C2(); Pentru ca această funcţie să aibă acces la membrii
clasei C2, se declară funcţie prieten clasei C2.
Sunt premise astfel conversii între o clasă şi un tip predefinit sau între două clase, dar nu de la
un tip predefinit la o clasă.
Constructorii cu un singur argument pot fi folosiţi ca funcţii de conversie de la tipul
argumentului la clasa constructorului. Acest efect nu este întotdeauna dorit, şi pentru a-l evita,
constructorul se declară precedat de cuvântul cheie explicit.
Dacă o metodă a unei clase foloseşte parametri care suferă conversii implicite, metoda se va
defini ca funcţie prietenă, nu ca funcţie membră.
Definim ca exemplu clasa Inch:
class Inch{
double inch;
double cm;
public:
explicit Inch(double i=0.) : inch(i){}; //ctor
void Inch_cm(){ cm=2.54*inch;};
operator int() const {return int(inch+0.5);}; //fctie conversie
}
9
Valeriu Iorga Programare în C / C++
Operatorul de inserţie <<, membru al clasei ostream serveşte pentru depunerea datelor de
tipuri predefinite în fluxul de ieşire cout. Supraîncărcarea operatorului permite depunerea de obiecte
ob în fluxul de ieşire f cu f << ob;
ostream& operator << (ostream& f, const clasa & ob);
Întrucât ambele funcţiile operatori întorc referinţe la fluxuri (prin return f), operatorii pot
fi înlănţuiţi.(f << o1 << o2;)
Funcţiile operator pentru supraîncărcarea operatorilor de intrare / ieşire se declară funcţii
prieten al clasei care interacţionează cu fluxul.
Exemplificăm cu clasele Cplx şi String:
class Cplx{
double re,im;
public:
friend ostream& operator<<(ostream& os, const Cplx& z);
friend istream& operator>>(istream& is, Cplx& z);
};
12. Probleme.
1. O expresie pătratică de o variabilă are forma: ax2+bx+c în care numerele a,b,c (coeficienţii)
au valori fixate, iar variabila x poate lua diferite valori.
Specificaţi,proiectaţi şi implementaţi clasa Patrat, care poate păstra informaţii
asupra unei expresii pătratice.
Un constructor implicit setează cei 3 coeficienţi la zero.
Se vor prevedea funcţii membru pentru:
- schimbarea coeficienţilor
- aflarea valorii curente a coeficienţilor
- evaluarea unei expresii pătratice pentru un x dat
10
Valeriu Iorga Programare în C / C++
4. Proiectaţi şi implementaţi clasa Vector care să permită lucrul cu vectori de elemente reale.
Constructorul clasei va avea un argument - dimensiunea vectorului şi va aloca emorie
pentru vector (în lipsa argumentului se ia implicit dimensiunea 10)
Se va asigura un destructor şi un constructor de copiere.
Se vor prevedea funcţii membri pentru:
- determinarea dimensiunii vectorului
- determinarea lungimii vectorului
- supraîncărcarea operatorilor +=, -=, pentru adunarea şi scăderea vectorului cu un alt vector dat ca
argument
Se va redefini operatorul >> ca funcţie prieten pentru citirea unui vector de la intrarea
standard
Se vor asigura funcţii nemembre pentru:
- testul de egalitate a doi vectori (supraîncărcarea operatorului ==)
- scrierea unui vector la ieşirea standard (supraîncărcarea operatorului <<)
- supraîncărcarea operatorilor +,- pentru a permite operaţii cu două argumente vectori
11
Valeriu Iorga Programare în C / C++
6. Proiectaţi şi implementaţi clasa String care să permită lucrul cu şiruri de caractere terminate
cu nul.
Constructorul clasei va avea un argument-dimensiunea adică numărul de caractere al şirului şi
va aloca memorie pentru şir (în lipsa argumentului se ia implicit dimensiunea 80).
Se va asigura un destructor şi un constructor de copiere.
Se vor prevedea funcţii membre pentru:
- determinarea lungimii şirului
- supraîncărcarea operatorului += pentru concatenarea şirului cu un alt şir dat ca argument
- determinarea pozitiei primei apariţii a unui şir dat ca argument în şirul dat
Se va redefini operatorul >> ca funcţie prieten pentru citirea unui şir de caractere de la intrarea
standard
Se vor asigura funcţii nemembre pentru:
- testul de egalitate a două şiruri (supraîncărcarea operatorului ==)
- comparaţia lexicografică a două şiruri date ca argumente (rezultat -1, 0 sau 1)
- scrierea unui şir la ieşirea standard (supraîncărcarea operatorului <<)
- supraîncărcarea operatorului + pentru a permite operaţia de concatenare a două şiruri date ca
argumente
- supraîncărcarea operatorului de atribuire.
- selectarea dintr-un şir a unui alt şir definit prin poziţie de început şi lungime
- ştergerea dintr-un şir a unui număr de caractere începând cu o poziţie dată.
12