Metode evoluate de programare Limbajele c şi C++


Supraîncărcarea operatorilor



Yüklə 1,64 Mb.
səhifə38/44
tarix07.04.2018
ölçüsü1,64 Mb.
#46828
1   ...   34   35   36   37   38   39   40   41   ...   44

16.7. Supraîncărcarea operatorilor




16.7.1. Metoda generală de supraîncărcare

Tipurile abstracte de date au avantajul de a îngloba datele şi operaţiile referitoare la aceste date. Operaţiile sunt exprimate cu ajutorul funcţiilor membru sau cu funcţii prietene. Totuşi ar fi mai avantajos dacă s-ar putea folosi şi operatori pentru exprimarea acestor operaţii. În exemplul referitor la clasa vector am văzut că putem scrie o funcţie membru care calculează suma a doi vectori, respectiv una care afişează vectorul, dar apelarea funcţiilor se face destul de complicat, în următoarea formă:


v.suma(t).afiseaza();
Ar fi mult mai simplu şi mai expresiv, dacă am putea scrie:
cout << v + t;
Pentru acesta trebuie supraîncărcat operatorul + pentru adunarea a doi vectori, şi operatorul << pentru afişarea unui vector la dispozitivul standard de ieşire stdout.

În limbajul C++ nu se pot defini operatori noi, dar există posibilitatea supraîncărcării operatorilor existenţi. Există şi câteva excepţii (de exemplu nu pot fi supraîncărcaţi următorii operatori: . :: şi ?:). Prin supraîncărcare nu se poate modifica faptul că operatorul este unar sau binar, nici prioritatea operatorilor şi nici direcţia de evaluare (asociativitatea) lor.

Supraîncărcarea operatorilor se face cu funcţii membru sau prietene specifice. Ele se comportă ca orice altă funcţie membru sau prieten, dar numele funcţiei trebuie să fie format din cuvântul cheie operator, după care pot fi eventual şi caractere albe, şi care va fi urmat de operatorul respectiv. Reamintim că în general spaţiile, taburile ('\t') şi caracterele de trecere la linie nouă ('\n') se numesc caractere albe.

Numărul parametrilor funcţiilor cu care se supraîncarcă operatorul se poate determina cu exactitate din faptul că operatorul este unar sau binar şi dacă funcţia este funcţie membru sau prieten. De exemplu dacă operatorul este binar şi supraîncărcarea se face printr-o funcţie membru atunci ea va avea un singur parametru, dacă se foloseşte o funcţie prieten atunci va avea doi parametri.

În cazul clasei vector operatorul + se poate supraîncărca în modul următor:
class vector {

private:


int* e; //elementele vectorului

int d; //dimensiunea vectorului

public:

vector(int* e1, int d1);



~vector() { delete [] e; }

vector operator +(vector& v1); //supraincarcarea

//operatorului +

void afiseaza();

};
vector vector::operator +(vector& v1)

{

if (d != v1.d)



{

cerr << "Eroare: dimensiune diferita";

exit(1);

}

int* x = new int[d];



for(int i = 0; i < d; i++)

x[i] = e[i] + v1.e[i];

vector y(x, d);

delete [] x;

return y;

}
Observăm că de fapt nu s-a făcut altceva decât s-a înlocuit numele funcţiei cu operator +. Operatorul se apelează în modul următor:


int main() {

int x[] = {1, 2, 3, 4, 5};

vector v(x, 5);

int y[] = {2, 4, 6, 8, 10};

vector t(y, 5);

(v+t).afiseaza();

return 0;

}

16.7.2. Supraîncărcarea operatorilor de atribuire

În genereal se poate folosi operatorul de atribuire (=) şi pentru obiecte. Prezentăm în continuare un exemplu pentru calcule cu fracţii. Fişierul fractie1.cpp:



Observăm că operatorul de atribuire se poate utiliza şi rezultatul este cel dorit, deci se afişează produsul celor două fracţii. Din păcate însă nu este întotdeauna aşa. Prezentăm un alt exemplu legat de clasa vector pentru ilustrarea acestui lucru. Fişierul vector5.cpp:


După o execuţie a programului rezultatele vor fi de exemplu:





Se observă că nu s-a obţinut ceea ce s-a dorit, deoarece prin instrucţiunea t = v s-a obţinut atribuirea adresei primului element al vectorului v datei membru e, corespunzătoare vectorului t şi nu s-au atribuit elementele în sine. De aceea orice modificare a elementelor vectorului v duce în continuare la modificarea elementelor vectorului t (în cazul nostru prin incrementarea elementelor vectorului v se incrementează şi elementele vectorului t).

Un alt neajuns este că nu s-a eliberat zona de memorie alocată iniţial elementelor vectorului t dar s-a eliberat de două ori cea rezervată pentru elementele vectorului v.

Explicaţia rezultatelor de mai sus constă în faptul că operatorul de atribuire (=) este supraîncărcat în mod implicit astfel încît să realizeze o copiere bit cu bit a datelor membru. În exemplul referitor la fracţii prin copierea bit cu bit se obţin rezultatele dorite dar în al doilea exemplu însă, nu. În general prin copierea bit cu bit nu se obţin rezultate corespunzătoare atunci când cel putin una dintre datele membru este un pointer. În acest caz supraîncărcarea operatorului de atribuire se poate face în modul următor:
class vector {

private:


int* e; //elementele vectorului

int d; //dimensiunea vectorului

public:

vector(int* e1, int d1);



vector(const vector& v1);

~vector();

void operator++(); //incrementarea elementelor

//vectorului

vector& operator =(const vector& v1);

//supraincarcarea

//operatorului

//de atribuire

void afiseaza(char* text); //afiseaza adresa

//primului element,

//dupa care afiseaza

//elementele

};
vector& vector::operator =(const vector& v1)

{

if (this != &v1)



{

delete[] e;

d = v1.d;

e = new int[d];

for(int i = 0; i < d; i++)

e[i] = v1.e[i];

}

return *this;



}
Operatorii de atribuire +=, -=, *=, /= nu sunt supraîncărcaţi în mod implicit, deci trebuie supraîncărcaţi de către programator. De exemplu operatorul += pentru clasa vector poate fi supraîncărcat în modul următor:
vector& vector::operator +=(vector& v1)

{

return *this = *this + v1;



}
Atragem atenţia asupra deosebirii dintre apelul operatorului de atribuire şi a constructorului implicit. De exemplu dacă se declară un obiect ob1 aparţinând clasei nume_clasa, în felul următor

nume_clasa ob1; //se apeleaza constructorul

//implicit, sau constructorul cu

//toti parametrii impliciti

atunci prin declaraţia

nume_clasa ob2 = ob1; //apelarea constructorului

//de copiere

se va apela constructorul de copiere şi nu operatorul de atribuire. Construcţia de mai sus este echivalentă cu:

nume_clasa ob2(ob1); //apelarea constructorului

//de copiere

Exisă totuşi asemănarea dintre operatorul de atribuire şi constructorul de copiere că, în cazul în care nu există constructor de copiere definit de programator, se va apela un constructor de copiere implicit care va realiza o iniţializare a obiectului printr-o copiere bit cu bit. Constructorul de copiere se va apela în general şi în următoarele două situaţii:

- dacă un parametru al unei funcţii este un obiect

- dacă o funcţie returnează un obiect

De aceea în cazul în care copierea bit cu bit nu dă rezultate corespunzătoare, este recomandat ca programatorul să definească un constructor de copiere, chiar şi în cazul în care nu se doreşte iniţializarea unui obiect printr-un alt obiect. Prezentăm un exemplu pentru ilustrare. Fişierul vector6.cpp:





Fişierul vector7.cpp:


Prin execuţie se obţine de exemplu:




Se observă că s-a apelat constructorul de copiere de două ori. Prima dată pentru crearea parametrului operatorului de adunare prin copierea obiectului vy. Destructorul acestui obiect s-a apelat automat după ce s-a părăsit funcţia membru corespunzătoare operatorului de adunare. Constructorul de copiere s-a apelat a doua oară atunci când s-a creat obiectul anonim vx+vy. Destructorul acestui obiect anonim s-a apelat însă numai la părăsirea funcţiei main. De aceea dacă elementele iniţiale a vectorului vz nu se folosesc, ar fi mai convenabil dacă vectorul vz s-ar iniţializa printr-un constructor. De exemplu cu funcţie main din fişierul vector8.cpp:




În acest caz constructorul de copiere se apelează tot de două ori, dar nu se mai crează obiectul anonim vx+vy.



16.7.3. Supraîncărcarea operatorilor de incrementare şi decrementare

În exemplul din fişierul vector5.cpp s-a supraîncărcat operatorul de incrementare. În funcţia main s-a executat instrucţiunea

++v;

pentru un obiect v al clasei vector. Dacă în locul acestei instrucţiuni am fi scris v++; atunci în faza de compilare ar fi apărut un mesaj de avertisment. De exemplu în Borland C++ apare mesajul: "Overloaded prefix 'operator ++' used as a postfix operator", deci operatorul ++ prefixat s-a folosit ca şi operator postfixat. Acelaşi mesaj de avertisment apare şi în următorul exemplu. Fişierul increm1.cpp:




În faza de compilare, mesajul de avertisment de mai sus apare de două ori, pentru cele două apeluri ale operatorului de incrementare postfixat. Prin executarea programului se obţine:




Din rezultatul de mai sus nu reiese că s-a apelat operatorul ++ postfixat de două ori. Operatorul de incrementare postfixat poate fi supraîncărcat cu o funcţie membru, care are un parametru de tip int. La apelarea unui operator postfixat, acest parametru va lua în mod automat valoarea zero. Deoarece valoarea parametrului nu se va folosi în corpul funcţiei membru, numele lui poate fi omis. Exemplul de mai sus se poate modifica în felul următor. Fişierul increm2.cpp:




În acest caz nu apar mesaje de avertisment la compilare. Rezultatul obţinut prin execuţie este:




Într-adevăr s-a afişat de fiecare dată mesajul corespunzător apelului operatorului prefixat respectiv postfixat. Menţionăm că operatorului de decrementare prefixat, respectiv postfixat se poate supraîncărca în mod analog.



Yüklə 1,64 Mb.

Dostları ilə paylaş:
1   ...   34   35   36   37   38   39   40   41   ...   44




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin