Sunteți pe pagina 1din 12

Valeriu Iorga Programare în C / C++

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.

3. Modalităţi de supraîncărcare a operatorilor.


O funcţie operator supraîncărcată poate fi introdusă ca funcţie membră (în general nestatică)
sau funcţie prieten (funcţie nemembră).
Declararea unei funcţii membră nestatică specifică următoarele:
1) funcţia poate accesa partea privată a declaraţiei clasei
2) funcţia este în domeniul clasei
3) funcţia trebuie apelată dintr-un obiect
O funcţie membră statică satisface numai primele două condiţii, în timp ce o funcţie prietenă a
unei clase satisface numai prima condiţie.
O funcţie operator având ca prim argument un tip primitiv nu poate fi funcţie membru.
Funcţiile care modifică reprezentarea unui obiect necesită acces la datele membri, deci trebuie
să aparţină clasei. Ele sunt funcţii membrecu argumente referinţe neconstante.
Dacă se doresc conversii implicite pentru termenii funcţiei operator, aceasta va fi funcţie
nemembră cu argumente referinţe constante. Aceste funcţii implementează operatorii care nu necesită
operanzi L-valori (adică se aplică tipurilor fundamentale). Aceştia sunt în general operatorii binari.
Necesitatea accesului la reprezentare determină definirea lor ca funcţii prieten.

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++

Cplx operator*(const Cplx& s, const Cplx& d){


return Cplx(s.re*d.re-s.im*d.im,s.re*d.im+s.im*d.re);
};
Cplx operator/(const Cplx& s, const Cplx& d){
double t=d.re*d.re+d.im*d.im;
return Cplx((s.re*d.re+s.im*d.im)/t ,
(s.im*d.re-s.re*d.im)/t);
};
int operator==(const Cplx& s, const Cplx& d){
return s.re==d.re && s.im==d.im;
};
int operator!=(const Cplx& s, const Cplx& d){
return s.re!=d.re || s.im!=d.im;
};
Cplx operator-(const Cplx& z){
return Cplx(-z.re, -z.im);
};
Cplx operator!(const Cplx& z){
return Cplx(z.re, -z.im);
};
Cplx& operator++(Cplx& z){ //prefix
return Cplx(z.re+1, z.im);
};
Cplx operator++(Cplx& z,int){ //postfix
Cplx t(z);
z.re+=1;
return t;
};
Pentru a face deosebirea între semnăturile funcţiilor operatori de incrementare prefix şi postfix
s-a introdus un argument fictiv pentru cel din urmă.
Incrementarea prefix întoarce valoarea incrementată, deci trebuie să fie o L-valoare (rezultatul
întors va fi o referinţă), în timp ce postincrementarea întoarce valoarea dinaintea incrementării.
5. Operatori supraîncărcaţi ca funcţii membri.
Funcţiilor membru li se transmite un argument implicit (ascuns) this (adresa obiectului, care
poate reprezenta primul termen), motiv pentru care un operator binar poate fi implementat printr-o
funcţie membru nestatică cu un singur argument (termenul din dreapta operatorului). a#b este
interpretat ca a.operator#(b)
O funcţie membru operator unar ca o funcţie membru nestatică fără argumente ( #a se
interpretează ca a.operator#(); pentru operatorul postfixat a# convenţia este
a.operator#(int) ).
În acest context, operatorii clasei Cplx se scriu:
Class Cplx{
double re, im;
public:
Cplx operator+(const Cplx& d);
Cplx operator-(const Cplx& d);
Cplx operator*(const Cplx& d);
Cplx operator/(const Cplx& d);
Cplx& operator+=(const Cplx& d);
Cplx& operator-=(const Cplx& d);
Cplx& operator*=(const Cplx& d);
Cplx& operator/=(const Cplx& d);

4
Valeriu Iorga Programare în C / C++

int operator==(const Cplx& d);


int operator!=(const Cplx& d);
Cplx& operator-();
Cplx& operator!();
Cplx& operator++();
Cplx operator++(int);
};

Cplx Cplx::operator+(const Cplx& d){


return Cplx(re+d.re, im+d.im);
};
Cplx Cplx::operator-(const Cplx& d){
return Cplx(re-d.re, im-d.im);
};
Cplx Cplx::operator*(const Cplx& d){
return Cplx(re*d.re-im*d.im, re*d.im+im*d.re);
};
Cplx Cplx::operator/(const Cplx& d){
double t=d.re*d.re+d.im*d.im;
return Cplx((re*d.re+im*d.im)/t, (im*d.re-re*d.im)/t);
};
Cplx& Cplx::operator+=(const Cplx& d){
if(this!=&d){
re+=d.re;
im+=d.im;
};
return *this;
};
Cplx& Cplx::operator-=(const Cplx& d){
if(this!=&d){
re-=d.re;
im-=d.im;
};
return *this;
};
Cplx& Cplx::operator*=(const Cplx& d){
if(this!=&d){
double t=re;
re=re*d.re-im*d.im;
im=t*d.im+im*d.re;
};
return *this;
};
Cplx& Cplx::operator/=(const Cplx& d){
if(this!=&d){
double t1=re, t2=d.re*d.re+d.im*d.im;
re=(re*d.re+im*d.im)/t2;
im=(im*d.re-t1*d.im)/t2;
};
return *this;
};
int Cplx::operator==(const Cplx& d){
return re==d.re && im==d.im;
};
int Cplx::operator!=(const Cplx& d){
return re!=d.re || im!=d.im;

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. Supraîncărcarea operatorului de atribuire.


Operaţia de atribuire simplă, dacă nu este supraîncărcată, realizează o copiere membru cu
membru.
Pentru obiectele care nu conţin date alocate dinamic la iniţializare, atribuirea prin copiere
membru cu membru funcţionează corect, motiv pentru care nu se supraîncarcă operatorul de atribuire.
Pentru clasele ce conţin date alocate dinamic, copierea membru cu membru, executată în mod
implicit la atribuire conduce la copierea pointerilor la datele alocate dinamic, în loc de a copia datele.
Operatorul de atribuire poate fi redefinit numai ca funcţie membră, el fiind legat de obiectul
din stânga operatorului =, o L-valoare, motiv pentru care va întoarce o referinţă la obiect.
O primă cerinţă la scrierea funcţiei operator= este evitarea autoatribuirii a=a. În acest
scop se efectuează testul this != &d (unde d este parametrul funcţiei -obiectul din dreapta
operatorului = .
Urmează apoi “curăţirea” membrului stâng (eliberarea memoriei alocate dinamic). Copierea
propriu zisă este făcută de obicei folosind constructorul de copiere.
Funcţia operator returnează o referinţă la obiectul modificat (return *this).
Exemplificăm pentru clasa String, definită astfel:
class String{
char* s;
int n;
public:
String(); //c(onstruc)tor implicit
String(const char* p); //ctor de initializare
String(const String& r); //cr de copiere
~String(); //dtor
String& operator=(const String& d);
String& operator=(const char* p);
};
//ctor de copiere
String::String& operator=(const String& d){
if(this != &d){ //evitare autoatribuire
if(s) //curatire
delete [] s;

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++

Exemplu1:Supraîncărcaţi operatorul new[], astfel încât să iniţializeze memoria alocată la


o valoare dată ca parametru.
void* C::operator new[](size_t dim, unsigned v){
int n = dim / sizeof(C);
C *p = ::new C[n];
for(int i=0; i<n; i++)
p[i].val = v; //clasa are data membra val
return p;
}
9. Supraîncărcarea operatorului apel de funcţie.
Apelul unei funcţii este o construcţie de forma:
nume_functie(lista_argumente);
desemnând operatorul binar() aplicat celor doi termeni nume_functie şi lista_argumente.
Numele funcţiei poate fi înlocuit printr-un pointer la funcţie:
(*pf)(lista_argumente);
Funcţia operator redefinit apel de funcţie va avea aşadar 2 parametri: un obiect şi o listă de
argumente. Implementată ca funcţie membră nestatică, primul parametru este transmis implicit, astfel
că un apel de forma:
obiect(lista_argumente);
este interpretat ca:
obiect.operator()(lista_argumente);
Supraîncărcarea operatorului () se utilizează la definirea functorilor (obiecte funcţii). Un
functor este un obiect pentru care se supraîncarcă operatorul apel de funcţie (operator()()) şi care
se comportă ca o funcţie.
În mod uzual obiectele funcţii se folosesc în locul pointerilor la funcţii. Funcţiile inline nu pot
folosi ca parametri pointeri la funcţii (care sunt apelaţi indirect), ci obiecte funcţii.
Operatorul apel de funcţie asigură o sintaxă uniformă pentru obiectele care se comportă ca şi
funcţiile.
Algoritmii generici din STL au printre argumente şi o biecte funcţii. Acestea pot fi:
- generatori = functori fără argumente
- funcţii unare = functori cu un argument
- funcţii binare = functori cu două argumente.
O funcţie unară booleană este un predicat, în timp ce o funcţie binară booleană este un
predicat binar.
De exemplu, pentru o dreaptă, definită prin tăietura în origine şi pantă putem defini clasa:
class Dreapta{
double m,y0;
public:
Dreapta(double m1=1, double y01=0): m(m1),y0(y01){} //ctor
double operator()(double x){ return y0+m*x; } //functor
};
Clasa va fi folosită într-o funcţie main() astfel:
Dreapta d1;
Dreapta d2(2., 3.);
double y1=d1(5.5); //y1=5.5
double y2=d2(1.5); //y2=2*1.5+3=6
Definiţi un obiect funcţie (functor) care generează termenul de rang n din şirul lui Fibonacci.
class Fibo{
public:
Fibo():x(0),y(1){}; //constructor
long operator()(int n); //supraincarcare operator functie

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
}

11. Supraîncărcarea operatorilor << şi >>.


Operatorii de intrare / ieşire pot fi supraîncărcaţi. Operatorul de extracţie >>, membru al clasei
istream serveşte pentru extragerea datelor de tipuri predefinite din fluxul de intrare cin. Prin
supraîncărcarea operatorului putem extrage din fluxul de intrare f, obiecte ob, scriind f >> ob;
Funcţia operator corespunzătoare are semnătura:
istream& operator >> (istream& f, clasa & ob);

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);
};

ostream& operator<<(ostream& os, const Cplx& z){


os << “(“ << z.re <<”,” << z.im << “)” << endl;
return os;
};
istream& operator>>(istream& is, Cplx& z){
is >> z.re >> z.im;
return is;
};
class String{
int n;
char *s;
public:
. . .
friend ostream& operator<<(ostream&os,const String& w);
friend istream& operator>>(istream& is, String& w);
};

ostream& operator<<(ostream& os,const String& w){


for(int i=0; i<w.n; i++)
os << w.s[i];
return os;
};
istream& operator>>(istream& is, String& w){
char zona[100];
is.get(zona,100);
w=zona;
return is;
};

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++

- determinarea numărului de rădăcini reale a unei expresii pătratice


Se vor redefini operatorii + şi * ca funcţii nemembre pentru: adunarea a două expresii
pătratice:
Patrat operator +(const Patrat &p1, const Patrat &p2);
- înmulţirea unei expresii pătratice cu o constantă
Patrat operator *(double k, const Patrat &p);

2. Proiectaţi şi implementaţi clasa Complex care să permită lucrul cu numere complexe.


Constructorul clasei va avea ca argumente partea reală, respectiv imaginară a numărului
complex (în mod implicit aceste valori se iau 0).
Se va asigura un constructor de copiere.
Se vor prevedea funcţii membri pentru:
- accesul la partea reală, respectiv imaginară a numărului complex
- supraîncărcarea operatorilot +=, -=, *=, /= pentru adunarea, scăderea, înmulţirea şi împărţirea
numărului complex cu un alt număr complex dat ca argument
- modulul numărului complex
- argumentul numărului complex
Se va redefini operatorul >> ca funcţie prieten pentru citirea unui număr complex de la
intrarea standard
Se vor asigura funcţii nemembru pentru:
- testul de egalitate a două numere complexe (supraîncărcarea operatorului ==)
- scrierea unui număr complex la ieşirea standard (supraîncărcarea operatorului <<)
- supraîncărcarea operatorilor +,-,*,/ pentru a permite operaţii cu două argumente numere
complexe
- supraîncărcarea operatorului de atribuire

3. Proiectaţi şi implementaţi clasa Raţional care să permită lucrul cu numere raţionale.


Constructorul clasei va avea două argumente: număratorul, respectiv numitorul numărului
raţional (constructorul poate avea şi un singur argument,caz în care al doilea se ia implicit 1,sau nici un
argument, caz în care se iau valorile implicite 0 şi 1).
Se va asigura un constructor de copiere.
Se vor prevedea funcţii membri pentru:
- accesul la număratorul, respectiv numitorul numărului raţional
- supraîncărcarea operatorilot +=, -=, *=, /= pentru adunarea, scăderea, înmulţirea şi împărţirea
numărului raţional cu un alt număr raţional dat ca argument
Se va redefini operatorul >> ca funcţie prieten pentru citirea unui număr raţional de la
intrarea standard
Se vor asigura funcţii nemembre pentru:
- testul de egalitate a două numere raţionale (supraîncărcarea operatorului ==)
- scrierea unui număr raţional la ieşirea standard (supraîncărcarea operatorului <<)
- supraîncărcarea operatorilor +,-,*,/ pentru a permite operaţii cu două argumente numere raţionale.
- supraîncărcarea operatorului de atribuire.

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++

- supraîncărcarea operatorului * pentru a permite calculul produsului scalar a doi vectori

5. Proiectaţi şi implementaţi clasa Matrice care să permită lucrul cu matrice pătrate de


elemente reale.
Constructorul clasei va avea un argument - dimensiunea, adică numărul de linii şi de
coloane al matricei, va aloca memorie pentru matrice (în lipsa argumentului se ia implicit dimensiunea
10) şi va permite accesul la elementele individuale prin indexare.
Se va asigura un destructor şi un constructor de copiere.
Se vor prevedea funcţii membre pentru:
- determinarea dimensiunii matricei
- calculul determinantului matricii
- supraîncărcarea operatorilor +=, -=, *=, pentru adunarea, scăderea, şi înmulţirea unei matrice cu
o altă matrice dată ca argument
- supraîncărcarea operatorului /= pentru calculul inversei matricei
Se va redefini operatorul >> ca funcţie prieten pentru citirea unei matrici de la intrarea
standard
Se vor asigura funcţii nemembre pentru:
- testul de egalitate a două matrice (supraîncărcarea operatorului ==)
- scrierea unei matrice la ieşirea standard (supraîncărcarea operatorului <<)
- supraîncărcarea operatorilor +,-,* pentru a permite operaţii cu două argumente matrice.

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

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