Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  

 
CATEGORII DOCUMENTE





AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


Supraincarcarea operatorilor

c

+ Font mai mare | - Font mai mic







DOCUMENTE SIMILARE

Trimite pe Messenger
Functii de citire si scriere a caracterelor
Functii recursive
Initializarea vectorilor de caractere
Analiza eficientei algoritmilor
adaugarea unui element in lista si stergerea primului element par
produsul elementelor pare din lista
Structuri de decizie (alternative, de selectie) - Structura de decizie: instructiunea if
Crearea si afisarea unei liste
Structura ciclica cu test initial - Instructiunea while
Setarea ecranului in mod text

Supraincarcarea operatorilor

1. Metoda generala de supraincarcare

Tipurile abstracte de date au avantajul de a ingloba datele si operatiile referitoare la aceste date. Operatiile sunt exprimate cu ajutorul functiilor membru sau cu functii prietene. Totusi ar fi mai avantajos daca s-ar putea folosi si operatori pentru exprimarea acestor operatii. In exemplul referitor la clasa vector am vazut ca putem scrie o functie membru care calculeaza suma a doi vectori, respectiv una care afiseaza vectorul, dar apelarea functiilor se face destul de complicat, in urmatoarea forma:

v.suma(t).afiseaza();

Ar fi mult mai simplu si mai expresiv, daca am putea scrie:

cout << v + t;

Pentru acesta trebuie supraincarcat operatorul + pentru adunarea a doi vectori, si operatorul << pentru afisarea unui vector la dispozitivul standard de iesire stdout.

In limbajul C++ nu se pot defini operatori noi, dar exista posibilitatea supraincarcarii operatorilor existenti. Exista si cateva exceptii (de exemplu nu pot fi supraincarcati urmatorii operatori: . :: si ?:). Prin supraincarcare nu se poate modifica faptul ca operatorul este unar sau binar, nici prioritatea operatorilor si nici directia de evaluare (asociativitatea) lor.

Supraincarcarea operatorilor se face cu functii membru sau prietene specifice. Ele se comporta ca orice alta functie membru sau prieten, dar numele functiei trebuie sa fie format din cuvantul cheie operator, dupa care pot fi eventual si caractere albe, si care va fi urmat de operatorul respectiv. Reamintim ca in general spatiile, taburile ('t') si caracterele de trecere la linie noua ('n') se numesc caractere albe.

Numarul parametrilor functiilor cu care se supraincarca operatorul se poate determina cu exactitate din faptul ca operatorul este unar sau binar si daca functia este functie membru sau prieten. De exemplu daca operatorul este binar si supraincarcarea se face printr-o functie membru atunci ea va avea un singur parametru, daca se foloseste o functie prieten atunci va avea doi parametri.

In cazul clasei vector operatorul + se poate supraincarca in modul urmator:

class vector

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

                                     //operatorului +

  void afiseaza();

};

vector vector::operator +(vector& v1)

  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;

}

Observam ca de fapt nu s-a facut altceva decat s-a inlocuit numele functiei cu operator +. Operatorul se apeleaza in modul urmator:

int main() ;

  vector v(x, 5);

  int y[] = ;

  vector t(y, 5);

  (v+t).afiseaza();

  return 0;

}

2. Supraincarcarea operatorilor de atribuire

In genereal se poate folosi operatorul de atribuire (=) si pentru obiecte. Prezentam in continuare un exemplu pentru calcule cu fractii. Fisierul fractie1.cpp:

#include <iostream.h>

class fractie ;

inline fractie::fractie(int numarator1 = 1,

                        int numitor1 = 0)

inline fractie fractie::operator *(fractie& r)

inline void fractie::afiseaza()

int main()

Observam ca operatorul de atribuire se poate utiliza si rezultatul este cel dorit, deci se afiseaza produsul celor doua fractii. Din pacate insa nu este intotdeauna asa. Prezentam un alt exemplu legat de clasa vector pentru ilustrarea acestui lucru. Fisierul vector5.cpp:

#include <iostream.h>

#include <stdlib.h>

class vector ;

vector::vector(int* e1, int d1)

vector::~vector()

void vector::operator++()

void vector::afiseaza(char* text)

int main() ;

  vector v(x, 5);

  int y[] = ;

  vector t(y, 5);

  v.afiseaza('v');

  t.afiseaza('t');

  t = v;

  cout << 'Dupa atribuirea t = v:n';

  t.afiseaza('t');

  ++v;

  cout

    << 'Dupa incrementarea elementelor '

    << 'vectorului v:' << endl;

  v.afiseaza('v');

  t.afiseaza('t');

  return 0;

}

Dupa o executie a programului rezultatele vor fi de exemplu:

Vectorul v (adresa primului element si elementele):

0x222a8

1 2 3 4 5

Vectorul t (adresa primului element si elementele):

0x222c8

20 40 60 80 100

Dupa atribuirea t = v:

Vectorul t (adresa primului element si elementele):

0x222a8

1 2 3 4 5

Dupa incrementarea elementelor vectorului v:

Vectorul v (adresa primului element si elementele):

0x222a8

2 3 4 5 6

Vectorul t (adresa primului element si elementele):

0x222a8

2 3 4 5 6

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x222a8

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x222a8

Se observa ca nu s-a obtinut ceea ce s-a dorit, deoarece prin instructiunea t = v s-a obtinut atribuirea adresei primului element al vectorului v datei membru e, corespunzatoare vectorului t si nu s-au atribuit elementele in sine. De aceea orice modificare a elementelor vectorului v duce in continuare la modificarea elementelor vectorului t (in cazul nostru prin incrementarea elementelor vectorului v se incrementeaza si elementele vectorului t).

Un alt neajuns este ca nu s-a eliberat zona de memorie alocata initial elementelor vectorului t dar s-a eliberat de doua ori cea rezervata pentru elementele vectorului v.

Explicatia rezultatelor de mai sus consta in faptul ca operatorul de atribuire (=) este supraincarcat in mod implicit astfel incit sa realizeze o copiere bit cu bit a datelor membru. In exemplul referitor la fractii prin copierea bit cu bit se obtin rezultatele dorite dar in al doilea exemplu insa, nu. In general prin copierea bit cu bit nu se obtin rezultate corespunzatoare atunci cand cel putin una dintre datele membru este un pointer. In acest caz supraincarcarea operatorului de atribuire se poate face in modul urmator:

class vector ;

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



  return *this;

}

Operatorii de atribuire +=,  -=, *=, /= nu sunt supraincarcati in mod implicit, deci trebuie supraincarcati de catre programator. De exemplu operatorul += pentru clasa vector poate fi supraincarcat in modul urmator:

vector& vector::operator +=(vector& v1)

Atragem atentia asupra deosebirii dintre apelul operatorului de atribuire si a constructorului implicit. De exemplu daca se declara un obiect ob1 apartinand clasei nume_clasa, in felul urmator

nume_clasa ob1; //se apeleaza constructorul

                //implicit, sau constructorul cu

                //toti parametrii impliciti

atunci prin declaratia

nume_clasa ob2 = ob1; //apelarea constructorului

                      //de copiere

se va apela constructorul de copiere si nu operatorul de atribuire. Constructia de mai sus este echivalenta cu:

nume_clasa ob2(ob1);   //apelarea constructorului

                       //de copiere

Exisa totusi asemanarea dintre operatorul de atribuire si constructorul de copiere ca, in cazul in care nu exista constructor de copiere definit de programator, se va apela un constructor de copiere implicit care va realiza o initializare a obiectului printr-o copiere bit cu bit. Constructorul de copiere se va apela in general si in urmatoarele doua situatii:

- daca un parametru al unei functii este un obiect

- daca o functie returneaza un obiect

De aceea in cazul in care copierea bit cu bit nu da rezultate corespunzatoare, este recomandat ca programatorul sa defineasca un constructor de copiere, chiar si in cazul in care nu se doreste initializarea unui obiect printr-un alt obiect. Prezentam un exemplu pentru ilustrare. Fisierul vector6.cpp:

#include <iostream.h>

#include <stdlib.h>

class vector ;

vector::vector(int* e1, int d1)

vector::vector(vector& v1)

vector::~vector()

vector vector::operator +(vector v1)

  int* x = new int[d];

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

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

  vector temp(x, d);

  temp.afiseaza('temp');

  return temp;

  //return vector(x, d);

}

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

  return *this;

}

void vector::afiseaza(char* text)

Fisierul vector7.cpp:

#include 'vector6.cpp'

int main() ;

  vector vx(x, 5);

  int y[] = ;

  vector vy(y, 5);

  int z[] = ;

  vector vz(z, 5);

  vx.afiseaza('vx');

  vy.afiseaza('vy');

  vz.afiseaza('vz');

  vz = vx + vy;

  cout << 'Dupa instructiunea vz = vx + vy:n';

  vz.afiseaza('vz');

  return 0;

}

Prin executie se obtine de exemplu:

Apel constructor.

Adresa primului element: 0x22cd8

Apel constructor.

Adresa primului element: 0x22cf8

Apel constructor.

Adresa primului element: 0x22d18

Vectorul vx (adresa primului element si elementele):

0x22cd8

1 2 3 4 5

Vectorul vy (adresa primului element si elementele):

0x22cf8

20 40 60 80 100

Vectorul vz (adresa primului element si elementele):

0x22d18

300 600 900 1200 1500

Apel constructor de copiere.

Adresa primului element: 0x22d38

Apel constructor.

Adresa primului element: 0x22d78

Vectorul temp (adresa primului element si elementele):

0x22d78

21 42 63 84 105

Apel constructor de copiere.

Adresa primului element: 0x22d98

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22d78

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22d38

Apel operator de atribuire ( = ).

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22d18

Dupa instructiunea vz = vx + vy:

Vectorul vz (adresa primului element si elementele):

0x22d18

21 42 63 84 105

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22d98

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22d18

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22cf8

Apel destructor.

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa: 0x22cd8

Se observa ca s-a apelat constructorul de copiere de doua ori. Prima data pentru crearea parametrului operatorului de adunare prin copierea obiectului vy. Destructorul acestui obiect s-a apelat automat dupa ce s-a parasit functia membru corespunzatoare operatorului de adunare. Constructorul de copiere s-a apelat a doua oara atunci cand s-a creat obiectul anonim vx+vy. Destructorul acestui obiect anonim s-a apelat insa numai la parasirea functiei main. De aceea daca elementele initiale a vectorului vz nu se folosesc, ar fi mai convenabil daca vectorul vz s-ar initializa printr-un constructor. De exemplu cu functie main din fisierul vector8.cpp:

#include 'vector6.cpp'

int main() ;

  vector vx(x, 5);

  int y[] = ;

  vector vy(y, 5);

  vx.afiseaza('vx');

  vy.afiseaza('vy');

  vector vz = vx + vy;

  cout << 'Dupa instructiunea vz = vx + vy:n';

  vz.afiseaza('vz');

  return 0;

}

In acest caz constructorul de copiere se apeleaza tot de doua ori, dar nu se mai creaza obiectul anonim vx+vy.

3. Supraincarcarea operatorilor de incrementare si decrementare

In exemplul din fisierul vector5.cpp s-a supraincarcat operatorul de incrementare. In functia main s-a executat instructiunea

++v;

pentru un obiect v al clasei vector. Daca in locul acestei instructiuni am fi scris v++; atunci in faza de compilare ar fi aparut un mesaj de avertisment. De exemplu in Borland C++ apare  mesajul: 'Overloaded prefix 'operator ++' used as a postfix operator', deci operatorul ++ prefixat s-a folosit ca si operator postfixat. Acelasi mesaj de avertisment apare si in urmatorul exemplu. Fisierul increm1.cpp:

#include <iostream.h>

#include <conio.h>

class Clasa

  Clasa& operator++();

  void scrie_x();

};

Clasa& Clasa::operator++()

 

void Clasa::scrie_x()

 

int main()

In faza de compilare, mesajul de avertisment de mai sus apare de doua ori, pentru cele doua apeluri ale operatorului de incrementare postfixat. Prin executarea programului se obtine:

x = 0

S-a apelat operatorul ++ prefixat

x = 1

S-a apelat operatorul ++ prefixat

S-a apelat operatorul ++ prefixat

x = 3

Din rezultatul de mai sus nu reiese ca s-a apelat operatorul ++ postfixat de doua ori. Operatorul de incrementare postfixat poate fi supraincarcat cu o functie membru, care are un parametru de tip int. La apelarea unui operator postfixat, acest parametru va lua in mod automat valoarea zero. Deoarece valoarea parametrului nu se va folosi in corpul functiei membru, numele lui poate fi omis. Exemplul de mai sus se poate modifica in felul urmator. Fisierul increm2.cpp:

#include <iostream.h>

#include <conio.h>

class Clasa

  Clasa& operator++();

  Clasa& operator++( int );

  void scrie_x();

};

Clasa& Clasa::operator++()

 

Clasa& Clasa::operator++( int )

 

void Clasa::scrie_x()

 

int main()

In acest caz nu apar mesaje de avertisment la compilare. Rezultatul obtinut prin executie este:

x = 0

S-a apelat operatorul ++ postfixat

x = 1

S-a apelat operatorul ++ prefixat

S-a apelat operatorul ++ postfixat

x = 3

Intr-adevar s-a afisat de fiecare data mesajul corespunzator apelului operatorului prefixat respectiv postfixat. Mentionam ca operatorului de decrementare prefixat, respectiv postfixat se poate supraincarca in mod analog.

DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 652
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Distribuie URL

Adauga cod HTML in site



Termeni si conditii de utilizare | Contact
© SCRIGROUP 2019. All rights reserved