This shows you the differences between two versions of the page.
poo-is-ab:laboratoare:04 [2024/10/20 17:08] razvan.cristea0106 [Funcții friend] |
poo-is-ab:laboratoare:04 [2025/01/19 22:28] (current) razvan.cristea0106 |
||
---|---|---|---|
Line 31: | Line 31: | ||
**Operatorul Ternar**: există un **singur** operator ternar în C++, cunoscut sub numele de **operator condițional (?:)**. Acesta utilizează **trei** operanzi și este folosit pentru a evalua o condiție și a alege între două valori, în funcție de rezultatul acelei condiții. | **Operatorul Ternar**: există un **singur** operator ternar în C++, cunoscut sub numele de **operator condițional (?:)**. Acesta utilizează **trei** operanzi și este folosit pentru a evalua o condiție și a alege între două valori, în funcție de rezultatul acelei condiții. | ||
- | În continuare vom prezenta un tabel cu operatorii din C++ unde vom vedea atât simbolurile cât și modul de asociere al acestora. | + | În continuare vom prezenta un tabel cu operatorii existenți în limbajul C++ pentru a putea vedea atât simbolurile cât și modul de asociere (aplicare) al acestora. |
^ Categoria de operatori ^ Simbolurile operatorilor ^ Mod de asociere (aplicare) ^ | ^ Categoria de operatori ^ Simbolurile operatorilor ^ Mod de asociere (aplicare) ^ | ||
- | | **Primari** | (), [], ., %%->%% | stânga - dreapta | | + | | **Primari** | ( ), [ ], ., %%->%% | stânga - dreapta | |
| **Unari** | ++, %%--%%, -, !, ~, (tip), &, *, sizeof | **dreapta - stânga** | | | **Unari** | ++, %%--%%, -, !, ~, (tip), &, *, sizeof | **dreapta - stânga** | | ||
| **Multiplicativi** | *, /, % | stânga - dreapta | | | **Multiplicativi** | *, /, % | stânga - dreapta | | ||
Line 120: | Line 120: | ||
În mod evident puteam declara și implementa o metodă simplă de afișare în loc să optăm pentru o funcție **friend**. Trebuie însă menționat faptul că este doar un exemplu didactic pentru a putea înțelege cum putem folosi funcțiile **friend** în limbajul C++. | În mod evident puteam declara și implementa o metodă simplă de afișare în loc să optăm pentru o funcție **friend**. Trebuie însă menționat faptul că este doar un exemplu didactic pentru a putea înțelege cum putem folosi funcțiile **friend** în limbajul C++. | ||
- | <note warning>Deși sunt declarate în interiorul clasei funcțiile **friend** se numesc **funcții** și **nu metode** datorită faptului că nu primesc **pointerul this** în lista de parametri. Cuvântul cheie **friend** se utilizează doar la declararea funcției pentru a înștința compilatorul că este vorba despre o funcție și nu despre o metodă, iar implementarea acesteia este **identică** cu a unei **funcții clasice**.</note> | + | <note warning>Deși sunt declarate în interiorul clasei **funcțiile friend** se numesc **funcții** și **nu metode** datorită faptului că **nu** primesc **pointerul this** în lista de parametri. Cuvântul cheie **friend** se utilizează **doar** la declararea funcției pentru a înștința compilatorul că este vorba despre o **funcție** și **nu** despre o **metodă**, iar implementarea acesteia este **identică** cu a unei **funcții clasice din C++**.</note> |
Vom folosi foarte mult acest tip de funcții după cum vom vedea în cele ce urmează la supraîncărcarea operatorilor limbajului C++. | Vom folosi foarte mult acest tip de funcții după cum vom vedea în cele ce urmează la supraîncărcarea operatorilor limbajului C++. | ||
Line 128: | Line 128: | ||
În această secțiune vom învăța cum vom putea specializa diverși operatori pentru o clasă astfel încât obiectele acesteia să se comporte similar cu variabilele care sunt declarate folosind **tipuri de date primitive (int, float, char, long, double, etc.)**. Clasa cu care vom lucra este **NrComplex** în care ne dorim să punem în evidență diferite variante de operatori supraîncărcați. | În această secțiune vom învăța cum vom putea specializa diverși operatori pentru o clasă astfel încât obiectele acesteia să se comporte similar cu variabilele care sunt declarate folosind **tipuri de date primitive (int, float, char, long, double, etc.)**. Clasa cu care vom lucra este **NrComplex** în care ne dorim să punem în evidență diferite variante de operatori supraîncărcați. | ||
- | Structura clasei **NrComplex** poate fi observata în codul de mai jos. | + | Structura clasei **NrComplex** poate fi observată în codul de mai jos. |
<code cpp> | <code cpp> | ||
Line 182: | Line 182: | ||
</code> | </code> | ||
- | <note>După cum am putut observa în fișierul **header** pentru clasa **NrComplex** am declarat un constructor cu parametri cu **valori implicite**. Acest **constructor special** ține locul de fapt a trei constructori. Dacă **nu** vom specifica niciun parametru câmpurile vor prima valoarea 0.0 care este cea **default**. Dacă specificăm un parametru atunci **doar** partea imaginară va primi valoarea 0.0 care este cea **implicită**.</note> | + | <note>După cum am putut observa în fișierul **header** pentru clasa **NrComplex** am declarat un constructor cu parametri cu **valori implicite**. Acest **constructor special** ține locul de fapt a trei constructori. Dacă **nu** vom specifica niciun parametru câmpurile vor primi valoarea 0.0 care este cea **default**. Dacă specificăm un parametru atunci **doar** partea imaginară va primi valoarea 0.0 care este cea **implicită**.</note> |
- | <note warning>Atunci când definim **funcții** sau **metode** care conțin parametri cu **valori implicite**, este esențial ca acești parametri să fie plasați **după** parametrii fără valori implicite în lista de argumente. Dacă această regulă **nu** este respectată, compilatorul va genera o eroare, deoarece nu va putea să determine corect care dintre parametri trebuie să primească valoarea implicită și care valoare este furnizată explicit la apelul funcției. Această regulă asigură claritatea și predictibilitatea modului în care sunt procesate argumentele funcției.</note> | + | <note warning>Atunci când definim **funcții** sau **metode** care conțin parametri cu **valori implicite**, este esențial ca acești parametri să fie plasați **după** parametrii fără valori implicite în lista de argumente. Dacă această regulă **nu** este respectată, compilatorul va genera o **eroare de ambiguitate**, deoarece **nu** va putea să determine **corect** care dintre parametri trebuie să primească valoarea implicită și care valoare este furnizată explicit la apelul funcției. Această regulă asigură **claritatea** și **predictibilitatea** modului în care sunt procesate argumentele funcției.</note> |
=== Operatori supraîncărcați ca funcții membre în clasă === | === Operatori supraîncărcați ca funcții membre în clasă === | ||
Line 190: | Line 190: | ||
În general alegem această variantă de supraîncărcare atunci când vrem să avem acces **direct** la membrii unei clase. Astfel, se pot manipula **direct** datele interne ale obiectului fără a fi necesare **metode acccesor** de tipul **getter/setter** sau mecanisme suplimentare pentru accesarea datelor private. | În general alegem această variantă de supraîncărcare atunci când vrem să avem acces **direct** la membrii unei clase. Astfel, se pot manipula **direct** datele interne ale obiectului fără a fi necesare **metode acccesor** de tipul **getter/setter** sau mecanisme suplimentare pentru accesarea datelor private. | ||
- | <note important>Operatorii **unari** sunt de regulă supraîncărcați ca **funcții membre**, deoarece aceștia operează întotdeauna asupra unui singur obiect, respectiv **obiectul curent**. În acest caz, obiectul curent este accesat **implicit** prin intermediul **pointerului this**, iar modificările sunt aplicate **direct** asupra sa. Această abordare oferă un control eficient asupra stării interne a obiectului, fără a necesita acces din exterior la membrii clasei.</note> | + | <note important>Operatorii **unari** sunt supraîncărcați **doar** ca **funcții membre**, deoarece aceștia operează întotdeauna asupra unui singur obiect, respectiv **obiectul curent**. În acest caz, obiectul curent este accesat **implicit** prin intermediul **pointerului this**, iar modificările sunt aplicate **direct** asupra sa. Această abordare oferă un control eficient asupra stării interne a obiectului, fără a necesita acces din exterior la membrii clasei.</note> |
== Supraîncărcarea operatorului ++ == | == Supraîncărcarea operatorului ++ == | ||
- | După cum bine știm există două variante pentru acest operator și anume **forma prefixată**, care presupune modificarea valorii **înainte** de a trece la următoarea operație, și respectiv **forma postfixată**, unde valoarea este **mai întâi folosită** și **apoi modificată** ulterior. | + | După cum bine știm există două variante pentru acest operator și anume **forma prefixată**, care presupune modificarea valorii **înainte** de a trece la următoarea operație, și respectiv **forma postfixată**, unde valoarea este **mai întâi folosită** și **ulterior modificată**. |
Prin urmare trebuie să avem **două** metode care reprezintă cele **două** forme ale acestui **operator unar**. Putem face acest lucru folosindu-ne de **polimorfism** după cum urmează în exemplul de cod de mai jos. | Prin urmare trebuie să avem **două** metode care reprezintă cele **două** forme ale acestui **operator unar**. Putem face acest lucru folosindu-ne de **polimorfism** după cum urmează în exemplul de cod de mai jos. | ||
Line 245: | Line 245: | ||
</code> | </code> | ||
- | <note important>Se poate observa ca la **forma postfixată** avem un parametru de care **nu** ne folosim. Acel parametru este **doar** pentru a asigura **polimorfismul**, compilatorul facând diferența între cele două forme.</note> | + | <note important>Se poate observa ca la **forma postfixată** avem un parametru de care **nu** ne folosim. Acel parametru este **doar** pentru a asigura **polimorfismul**, compilatorul făcând distincția între cele două variante de operator de incrementare.</note> |
- | Pentru operatorul de decrementare se aplică aceleași exact aceeași pași, încercați să îl implementați voi pentru a putea înțelege mai bine. | + | Pentru **operatorul de decrementare** se aplică aceleași **exact** aceeași pași, încercați să îl implementați voi pentru a putea înțelege mai bine cum funcționează conceptul de **overloading**. |
== Supraîncărcarea operatorului ! == | == Supraîncărcarea operatorului ! == | ||
Line 299: | Line 299: | ||
== Supraîncărcarea operatorilor == și != == | == Supraîncărcarea operatorilor == și != == | ||
- | **Operatorul %%==%%** este folosit pentru a testa egaliatetea dintre doi operanizi, deci prin urmare trebuie să returneze o valoare de adevăr (**true** sau **false**). Îl supraîncârcăm ca funcție membră, deoarece avem deja un parametru existent, și anume **pointerul this**, la care mai adăugăm un alt parametru care reprezintă **obiectul cu care facem comparația**. | + | **Operatorul %%==%%** este folosit pentru a testa egaliatetea dintre doi operanzi, deci prin urmare trebuie să returneze o valoare de adevăr (**true** sau **false**). Îl supraîncârcăm ca funcție membră, deoarece avem deja un parametru existent, și anume **pointerul this**, la care mai adăugăm un alt parametru care reprezintă **obiectul cu care facem comparația**. |
Același lucru putem spune și despre **operatorul %%!=%%**, numai că el face exact **opusul** a ceea ce face operatorul de testare a egalității între doi operanzi, adică verifică dacă valorile celor doi termeni sunt **diferite**. | Același lucru putem spune și despre **operatorul %%!=%%**, numai că el face exact **opusul** a ceea ce face operatorul de testare a egalității între doi operanzi, adică verifică dacă valorile celor doi termeni sunt **diferite**. | ||
Line 345: | Line 345: | ||
bool NrComplex::operator!=(const NrComplex& z) const | bool NrComplex::operator!=(const NrComplex& z) const | ||
{ | { | ||
- | return this->real != z.real && this->imaginar != z.imaginar; | + | return this->real != z.real || this->imaginar != z.imaginar; |
} | } | ||
</code> | </code> | ||
Line 563: | Line 563: | ||
==== ==== | ==== ==== | ||
- | Codul cu implementările operatorilor prezentați pentru clasa **NrComplex** poate fi descărcat de {{:poo-is-ab:laboratoare:complex_overloading.zip|aici}}. | + | Codul complet cu implementările operatorilor prezentați pentru clasa **NrComplex** poate fi descărcat de {{:poo-is-ab:laboratoare:complex_overloading.zip|aici}}. |
- | <note warning>În limbajul C++ **nu** este permisă supraîncărcarea operatorilor următori: | + | <note warning>În limbajul C++ **nu** este permisă supraîncărcarea următorilor operatori: |
* de rezoluție **"::"** | * de rezoluție **"::"** | ||
* de acces la membrii unei clase/structuri **"."** | * de acces la membrii unei clase/structuri **"."** | ||
Line 576: | Line 576: | ||
==== Concluzii ==== | ==== Concluzii ==== | ||
- | În cadrul acestui laborator, am descoperit **importanța supraîncărcării operatorilor** într-o clasă și modul în care acest proces ne permite să efectuăm diverse operații într-un mod **intuitiv**, la fel cum procedăm și cu **tipurile de date standard (int, float, char,...)**. Prin **supraîncărcarea operatorilor**, am reușit să **îmbunătățim lizibilitatea și ușurința** în utilizarea claselor personalizate, oferind posibilitatea de a efectua **operații** cum ar fi **adunarea**, **scăderea** sau **compararea** obiectelor de tipul definit de noi. | + | În cadrul acestui laborator, am descoperit **importanța supraîncărcării operatorilor** într-o clasă și modul în care acest proces ne permite să efectuăm diverse operații într-un mod **intuitiv**, la fel cum procedăm și cu **tipurile de date standard** (**int**, **float**, **char**,...). Prin **supraîncărcarea operatorilor**, am reușit să **îmbunătățim lizibilitatea și ușurința** în utilizarea claselor personalizate, oferind posibilitatea de a efectua **operații** cum ar fi **adunarea**, **scăderea** sau **compararea** obiectelor de tipul definit de noi. |
- | Am înțeles, de asemenea, când este necesar să supraîncărcăm un operator ca **funcție membră** a unei clase și când este mai potrivit să îl supraîncărcăm ca **funcție friend**. Operatorii care au nevoie de **acces direct** la membrii clasei, cum ar fi **operatorii unari** sau **operatorul de asignare**, sunt adesea implementați ca **funcții membre**. În schimb, operatorii care implică obiecte de **diferite tipuri** (de exemplu, un obiect al clasei noastre și un tip fundamental precum **int** sau **double**) pot fi implementați mai eficient ca **funcții friend**, pentru a permite accesul din exterior la membri privați **fără** a compromite **încapsularea**. | + | Am înțeles, de asemenea, când este necesar să **supraîncărcăm** un operator ca **funcție membră** a unei clase și când este mai potrivit să îl supraîncărcăm ca **funcție friend**. Operatorii care au nevoie de **acces direct** la membrii clasei, cum ar fi **operatorii unari** sau **operatorul de asignare**, sunt adesea implementați ca **funcții membre**. În schimb, operatorii care implică obiecte de **diferite tipuri** (de exemplu, un obiect al clasei noastre și un tip fundamental precum **int** sau **double**) pot fi implementați mai eficient ca **funcții friend**, pentru a permite accesul din exterior la membri privați **fără** a compromite **încapsularea datelor clasei**. |