• Nenhum resultado encontrado

Supraîncărcarea operatorilor (continuare)

N/A
N/A
Protected

Academic year: 2023

Share "Supraîncărcarea operatorilor (continuare) "

Copied!
8
0
0

Texto

(1)

PROGRAMARE ORIENTATĂ PE OBIECTE

Supraîncărcarea operatorilor (continuare)

Supraîncărcarea operatorilor de conversie

La evaluarea unei expresii se efectuează conversii implicite pentru tipurile predefinite. Deoarece nu există conversii implicite de la/spre o clasă la/de la un tip predefinit, programatorul poate defini conversiilor explicite prin supraîncărcarea unui operator de conversie al clasei (operatorul “cast”) sau prin intermediul constructorilor.

Conversia unui obiect din clasa idClasa la un tip primitiv sau la un tip definit se realizează definind în clasa idClasa o funcţie membru nestatică de conversie.

Sintaxă pentru a realiza conversia de la tipul idClasa la tipul TipData.

class idClasa{

...

operator TipData ();//TipData este un nume de tip ...

};

Important: Nu se 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.

Apelul explicit al funcţiei de conversie de la un obiect din clasa idClasa la tipul TipData se specifică prin TipData(obiect) sau (TipData) obiect.

Conversia unui tip TipData la clasa idClasa se face prin intermediul constructorilor cu un singur argument. Pentru a evita eventuale efecte nedorite, constructorul se declară precedat de cuvântul cheie explicit. Constructorii declarați explicit pentru conversie trebuie să aibă un singur argument, de tipul dorit pentru conversie.

Exemplu

class NumarRational{

int x, y;

public:

NumarRational(int x = 0, int y=1){

this->x = x;

this->y = y;

}

explicit NumarRational(float f){

x=f;

y=1;

}

(2)

friend ostream& operator<<(ostream& out, const NumarRational& nr){

out << nr.x << "/"<< nr.y;

return out;

}

operator float()const{ //conversie NumarRational->float return x/(float)y;

} };

//functii de test void functie1(float r){

cout << "Apel functie1( "<< r << " )\n";

}

void functie2(NumarRational nr){

cout << "Apel functie2 ( "<<nr << " )\n";

}

void main() {

NumarRational r1(1,2), r2(3,4);

float f1, f2;

f1 = (float) r1;//conversie explicita cout << "f1="<< f1 << endl;

f2 = r2;//conversie implicita la atribuire cout << "f2="<< f2 << endl;

functie1(f1);//apel fara conversie

functie1(r1);//conversie la transferul parametrilor f1 = f1 + r1;//conversie implicita r1->float

cout << "f1="<< f1 << endl;

f2 = r1 + r2;//conversie implicita r1,r2->float cout << "f2="<< f2 << endl;

f1 = r2 + 3.21;//conversie implicita r2 -> float -> double cout << "f1="<< f1 << endl;

r1 = (NumarRational)f1;//conversie explicita cout << "r1="<<r1<< endl;

r2 = r1;//conversie implicita la atribuire cout << "r2="<<r2<< endl;

functie2(f2);//conversie implicita la transfer de parametri }

Supraîncărcarea operatorilor new şi delete.

Operatorii unari new și delete pot fi supraîncărcați pentru a realiza operații specializate de alocare/eliberare dinamica a memoriei.

Funcția operator new trebuie s[ primeasc[ un argument de tipul size_t care să precizeze dimensiunea ]n octeți a obiectului alocat și să returneze un pointer de tip void conținând adresa zonei alocate (sau sa returneze NULL daca eșuează).

Sintaxă:

void *operator new(size_t);

Important: Chiar dacă parametrul de tip size_t este obligatoriu, calculul dimensiunii obiectului în cauză și generarea sa se face de către compilator. Tipul size_t este definit in stdlib.h.

(3)

După alocarea dinamică este apelat constructorul clasei pe care o realizează. Pointerul universal (void*) returnat are ca valoare adresa de început a zonei de memorie alocată.

Funcția operator delete trebuie să primească ca prim parametru un pointer de tipul clasei în cauză sau void, continând adresa obiectului de distrus, și un al doilea parametru, optional, de tip size_t.

Functia nu intoarce nici un rezultat.

Sintaxă:

void operator delete(void *, size_t);

Operatorul redefinit delete apelează întotdeauna destructorul clasei înainte de a elibera memoria alocată dinamic.

Operatorii new și delete pot fi supraîncărcați global, astfel încat orice utilizare a lor să folosească versiunea supraîncărcată, sau pot fi supraîncărcăți pentru o anumită clasă, și în această situație funcțiile operator trebuie să fie membre statice ale clasei. Funcțiile membre ce supradefinesc acești operatori, fiind de interes general pentru clasa respectivă, sunt automat statice, fără să fie nevoie de utilizarea specificatorului static. Oricum operatorul new nu ar putea fi supraîncărcat prin funcție nestatică deoarece el nu ar putea aparține unui obiect pe care tot el să-l și aloce. La întalnirea unuia dintre operatorii new sau delete pentru un anumit tip, compilatorul verifică mai întâi dacă acesta a fost supraîncărcat pentru clasa respectivă. Dacă a fost supraîncărcat, se folosește versiunea supraîncărcată a clasei; dacă nu, este apelat operatorul new sau delete global.

Exemplu

//Supraincarcarea operatorului new astfel incat sa initializeze memoria alocata la 0 void* Exemplu::operator new(size_t dim){

//Parametrul dim va contine numarul de octeti necesar pentru a memora // obiectul pentru care se face alocarea

void *p = new char[dim];

return memset(p, 0, sizeof(Exemplu));

}

// Supraincarcarea operatorului new astfel incat sa initializeze memoria alocata la o //valoare data ca parametru

void* Exemplu::operator new[](size_t dim, unsigned v){

int n = dim / sizeof(Exemplu);

Exemplu *p = ::new Exemplu [n];

for(int i=0; i<n; i++){

...

}

return p;

}

Important: Când operatorii new și delete sunt funcții membre ale unei clase, operatorii astfel supraîncărcați vor fi folosiți numai pentru obictele clasei , iar când se utilizează operatorii new și delete pentru alte tipuri de date se va apela new și delete impliciți din C++.

(4)

Exemplu

class Localitate{

int longitudine,latitudine;

public:

Localitate(int lg,int lt){longitudine=lg; latitudine=lt;}

void *operator new (size_t dim){

return malloc(dim);

}

void operator delete(void *p){

free(p);

} };

Localitate *p1,*p2;

p1=new Localitate(20,99); //apel new supraincarcat if(!p1){

cout<<"EROARE DE ALOCARE "<<endl;

}

delete p1; //apel delete supraincarcat

int *f=new int[10]; //apel new implicit din C++ nu cel supraincarcat delete f; //apel delete implicit din C++ nu cel supraincarcat

Operatorii new și delete se pot redefini și global prin supraincarcarea acestor operatori în afara oricarei declarații de clasă .

Important: Când sunt supraîncărcați prin funcție independentă declarată global, definiția inițială a operatorului new și delete nu mai este recunoscută pentru nici un tip de bază sau clasă , alocarea și gestionarea memoriei revenindu-i în exclusivitate programatorului, prin urmare sunt ignorați new și delete impliciti din C++.

Exemplu

class Localitate{

int longitudine,latitudine;

public:

Localitate(int lg,int lt){longitudine=lg; latitudine=lt;}

};

//new global (independent)

void *operator new (unsigned dim){

return malloc(dim);

}

//delete global(independent) void operator delete(void *p){

free(p);

}

void main() {

Localitate *p1,*p2;

p1=new Localitate(20,99);

if(!p1){

cout<<"EROARE DE ALOCARE "<<endl;

}

delete p1;

double *f=new double; //foloseste de asemenea new supraincarcat nu pe cel implicit if(!f){

(5)

cout<<"EROARE DE ALOCARE "<<endl;

}

*f=10.111;

cout<<*f<<endl; //10.111

delete f; //foloseste delete supraincarcat nu pe cel implicit

}

Pentru cazul matricelor trebuie să supraîncărcăm operatorii new[] și delete[]. Pentru alocare este apelată automat funcția constructor a fiecărui obiect al matricei. Operatorul delete[] apelează repetat destructorul clasei pentru fiecare membru al matricei.

Sintaxă

void *operator new [ ](unsigned dim){

... //efectueaza alocarea return pointer_la _memorie;

}

void operator delete [ ] (void *p){

free( p);

}

Exemplu

class Localitate{

int longitudine,latitudine;

public:

Localitate(int lg=0,int lt=0){longitudine=lg; latitudine=lt;}

void scrie(){cout<<longitudine<<" "<<longitudine<<endl;}

void *operator new (size_t dim){

return malloc(dim);

}

void operator delete(void *p){

free(p);

}

void *operator new [ ](size_t dim);

void operator delete [ ] (void *p);

};

void *Localitate::operator new[ ](size_t dim){

return malloc(dim);

}

void Localitate::operator delete[ ](void *p){

free(p);

}

void main(){

Localitate *p1,*p2;

p1=new Localitate(20,99);//aloca memorie pt.un obiect if(!p1){

cout<<"EROARE DE ALOCARE "<<endl;

}

p2=new Localitate[3]; //aloca memorie pt.o matrice

(6)

cout<<"EROARE DE ALOCARE "<<endl;

}

for(int i=0;i<3;i++) p2[i].scrie(); //0 0 0 0 0 0 delete p1; //elibereaza un obiect

delete [ ] p2; //elibereaza o matrice

}

Important: Nu este permisă mixarea celor două mecanisme de alocare și eliberare de memorie (cu functii malloc( ) și free( ), respectiv cu operatorii new și delete ) adica alocare cu malloc( ) și dezalocare cu delete( ) sau alocare cu new și dezalocare cu free( ) .

Supraîncărcarea operatorului de indexare

Operatorul de indexare este un operator binar care are ca prim termen obiectul care se indexează, iar ca al doilea termen indicele: obiect [ indice ] şi este interpretat ca: obiect.operator[ ](indice) . Primul termen, transmis implicit prin this este obiectul asupra căruia se execută indexarea iar al doilea reprezintă indicele, care poate fi de orice tip (spre deosebire de indicii predefiniţi).

Important: Acest operator nu poate fi supraîncărcat folosind funcții friend, funcţia operator de indexare trebuind să fie funcţie membră nestatică. Valoarea întoarsă de funcţia operator este o referinţă la elementul indexat din obiect.

Exemplu

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;

};

Supraîncărcarea operatorului apel de funcţie

Aşa cum se definesc pointerii către obiecte de un anumit tip, se pot defini pointeri şi către funcţii (conţin adrese ale funcţiilor), numele unei funcţii reprezentând un pointer către acea funcţie.

(7)

Exemplu

int suma(double x,double y){

return (int)(x+y);

}

int (*p)(double,double);

Prin această declaraţie înţelegem că s-a definit p un pointer către funcţii care au două argumente de tip double şi returnează un întreg.

Sunt posibile declaraţiile:

p=suma;

p(7,9);

În exemplul următor vom defini o funcţie care primeşte drept argument o altă funcţie (prin intermediul pointerilor).

Exemplu

int suma(double x,double y){

return (int)(x+y);

}

int produs(double x,double y){

return (int)(x*y);

}

int calcul(int (*p)(double,double),double x,double y){

return p(x,y);

}

void main(){

cout<<calcul(suma,7,9);

cout<<calcul(produs,7,9);

}

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.

Având în vedere că 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);

(8)

Exemplu

class Dreapta{

double m,y0;

public:

Dreapta(double m=1, double y0=0){//Constructor this->m=m;

this->y0=y0;

}

double operator()(double x){ return y0+m*x; }//functor };

void main(){

Dreapta d(2, 3);

double y1=d(1.1);//y1=2*1.1=2.2 double y2=d(1.5);//y2=2*1.5+3=6 }

class Fibo{

long x0, x1;

public:

Fibo():x0(0),x1(1){}; //constructor

long operator()(int n); //supraincarcare operator functie };

long Fibo::operator ()(int n){

for(int i=2; i<=n; i++){

long z=x0+x1;

x0=x1;

x1=z;

};

return x0;

};

Important: Pentru utilizarea altor operatori (ex. +,-,*,/,++, -- etc. ) se pot urmări exemplele prezentate în cadrul laboratoarelor.

Referências

Documentos relacionados

Esse fenômeno pode ser modelado por meio da elaboração de um etnomodelo dialógico que tem como objetivo traduzir uma determinada prática matemática (método