Differences

This shows you the differences between two versions of the page.

Link to this comparison view

poo-is-ab:laboratoare:05 [2024/11/11 10:14]
razvan.cristea0106 [Moștenirea între două clase]
poo-is-ab:laboratoare:05 [2025/10/11 20:12] (current)
razvan.cristea0106 [Supraîncărcarea operatorilor]
Line 1: Line 1:
-===== Laborator 05 - Moștenire simplă =====+===== Laborator 05 - Supraîncărcarea operatorilor ​=====
  
 **Autor: Răzvan Cristea** **Autor: Răzvan Cristea**
Line 7: Line 7:
 Studentul va fi capabil la finalul acestui laborator să: Studentul va fi capabil la finalul acestui laborator să:
  
-  * recunoască și să înțeleagă conceptul de moștenire între două clase +  * recunoască și să înțeleagă conceptul de supraîncărcare (overloading) 
-  * construiască legături între clase folosind relația de tip "​is-a"​ (relație de specializare) +  * știe când un operator trebuie supraîncărcat fie ca funcție membră fie ca funcție friend ​ 
-  * folosească membrii marcați cu protected și să înțeleagă diferențele dintre accesul public, privat și protejat în moștenire +  * supraîncarce operatorii aritmetici pentru o clasă 
-  * aplice principiile ​de reutilizare a codului prin extinderea funcționalității clasei ​de bază în clasa derivată+  * supraîncarce operatorii logici ​de comparație 
 +  * supraîncarce operatorii ​de flux pentru citire și afișare
  
 ==== Introducere ==== ==== Introducere ====
  
-În acest laborator ​vom extinde lucrul cu claseintroducând conceptul ​de **relații între clase**. Vom construi și analiza legături între două clasefolosind relațiile de tip **"​is-a"​** și **"has-a"**dar vom pune accentul mai mult pe relația de **"​is-a"​**. Relația de tip **"​is-a"​** reprezintă o formă de moștenireun principiu fundamental al **POO**, prin care o **clasă derivată (subclasă)** preia **proprietățile** și **comportamentele** ​unei **clase de bază (superclasă)**+În acest laborator, ​ne vom concentra pe aprofundarea conceptului ​de **overloading (supraîncărcare)**, un aspect esențial al **POO**. Așa cum am introdus deja în [[poo-is-ab:​laboratoare:​03|laboratorul 3]]atunci când am discutat despre ​**polimorfism**, am înțeles că supraîncărcarea se referă la posibilitatea ​de a defini mai multe funcții cu același numedar cu semnături diferite. Acest mecanism se aplică atât funcțiilor libere, cât și metodelor în cadrul ​unei clase.
  
-Acest tip de relație ne permite să definim ​**ierarhii de clase**, să **reutilizăm codul** și să **extindem** ​funcționalitățile claselor, oferind un model de organizare flexibil ​și scalabil**Moștenirea** funcționează similar cu cea din viața reală: ​**clasa derivată** preia proprietățile ​și comportamentele **clasei ​de bază**, dar poate adăuga și **comportamente noi** sau **modifica** pe cele existenteAstfel, **moștenirea** facilitează **extensibilitatea** ​și **întreținerea** coduluireducând duplicarea ​și oferind un mod eficient de a gestiona complexitatea în proiecte de mari dimensiuni. +Este important ​de subliniat că supraîncărcarea nu schimbă comportamentul fundamental al unei **funcții** sau **metode**, ci oferă alternative prin care acestea pot fi apelate, în funcție de tipul și numărul parametrilorCu alte cuvinte, ​**funcția** sau **metoda** își păstrează scopul ​de bază, dar poate trata diverse scenarii sau tipuri de date fără a necesita ​**nume diferite**. Această flexibilitate contribuie la creșterea lizibilității codului ​și la reducerea redundanțeipermițând programatorilor să scrie cod mai clar și mai modular.
-==== Moștenirea între două clase ====+
  
-Așa cum am menționat anterior, **moștenirea** este un principiu fundamental al **POO** care permite unei **clase derivate** să preia atât **proprietățile (atributele)** cât și **comportamentele (funcțiile membre)** unei **clase părinte**. Prin acest mecanism, **clasa derivată** poate să extindă funcționalitatea moștenită prin adăugarea de noi atribute șmetode sau prin redefinirea celor existente. Scopul principal al **moștenirii** este de a promova **reutilizarea codului** și de a permite o **extensie** naturală a funcționalităților inițiale, astfel încât să se creeze o structură mai **flexibilă** ​și mai **ușor de întreținut** în cadrul ​aplicațiilor.+Pe parcursul acestui laborator, vom explora în detaliu ​cum funcționează **supraîncărcarea operatorilor** în C++ și cum poate fi utilizată eficient în cadrul claselor pentru a îmbunătăți funcționalitatea ​și flexibilitatea ​aplicațiilor ​la care lucrăm.
  
-Înainte ​de a explica moștenirea ​între două clase vom face o scurtă recapitulare ​noțiunilor deja învățate în cadrul laboratoarelor anterioarePentru acest laborator propunem clasa **Locuinta** care are ca și membri ​**pret (de tip float)** și **adresa (șir de caractere alocat dinamic)**.+==== Operatorii limbajului C++ ==== 
 + 
 +Când discutăm despre operatori în C++, primul lucru de care trebuie să ținem cont este faptul că aceștia operează asupra unor **operanzi**. În funcție de **numărul de operanzi** asupra cărora acționează,​ operatorii pot fi clasificați în trei mari categorii dupa cum urmează în descrierea de mai jos. 
 + 
 +**Operatori Unari**: acești operatori au nevoie de **un singur** operand pentru ​a-și îndeplini funcția. Ei sunt folosiți în mod frecvent pentru operații simple precum negarea, incrementarea sau decrementarea valorii operandului. Exemple comune includ **++ (incrementare)**,​ **%%--%% (decrementare)**,​ **! (negare logică)** și **- (negare aritmetică)**. 
 + 
 +**Operatori Binari**: aceștia necesită **doi** operanzi și sunt cei mai utilizați operatori în programare. Acești operatori includ **adunarea (+)**, **scăderea (-)**, **înmulțirea (*)**, **împărțirea (/)**, dar și operatori logici precum **&&​ (și logic)**, **|| (sau logic)** și **operatori de comparație (==, !=, <, >)**. 
 + 
 +**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 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) ​             ^ 
 +| **Primari** ​           | ( ), [ ], ., %%->%% |         ​stânga - dreapta ​        | 
 +| **Unari** ​             | ++, %%--%%, -, !, ~, (tip), &, *, sizeof |         ​**dreapta - stânga** ​        | 
 +| **Multiplicativi** ​    | *, /, %                         ​| ​        ​stânga - dreapta ​        | 
 +| **Adunare, scădere** ​  | +, -                            |         ​stânga - dreapta ​        | 
 +| **Deplasare (nivel bit)** | %%<<​%%,​ %%>>​%% ​              ​| ​        ​stânga - dreapta ​        | 
 +| **Relaționali** ​       | <, >, %%<=%%, %%>​=%% ​           |         ​stânga - dreapta ​        | 
 +| **Testare egalitate** ​ | ==, !=                          |         ​stânga - dreapta ​        | 
 +| **ȘI (nivel bit)** ​    | &                               ​| ​        ​stânga - dreapta ​        | 
 +| **SAU exclusiv (nivel bit)** | %%^%%                     ​| ​        ​stânga - dreapta ​        | 
 +| **SAU inclusiv (nivel bit)** | %%|%%                     ​| ​        ​stânga - dreapta ​        | 
 +| **ȘI logic** ​          | %%&&​%% ​                         |         ​stânga - dreapta ​        | 
 +| **SAU logic** ​         | %%||%% ​                         |         ​stânga - dreapta ​        | 
 +| **Condițional (ternar)**| ?:                             ​| ​        ​stânga - dreapta ​        | 
 +| **Atribuire** ​         | =, +=, -=, *=, /=, %=, %%<<​=%%,​ %%>>​=%%,​ &=, %%^=%%, %%|=%% |         ​**dreapta - stânga** ​        | 
 +| **Virgulă** ​           | ,                               ​| ​        ​stânga - dreapta ​        | 
 + 
 +<note important>​Pentru operatorii din tabelul de mai sus am ales această ordine pentru ​putea stabili cu ușurință **prioritățile** fiecăruia dintre ei. Astfel cea mai **mică** prioritate o are operatorul **virgulă**, ​în timp ce cea mai **mare** prioritate o au **operatorii primari**.</​note>​ 
 + 
 +==== Funcții friend ==== 
 + 
 +În C++ funcțiile ​**friend** sunt acele funcții ​care au aces la zona **privată** a clasei, dar și la cea **protected**. Deși o funcție friend strică **principiul încapsulării datelor**, trebuie menționat că acest lucru este controlat.  
 + 
 +<note tip>​Pentru a înțelege mai bine rolul funcțiilor **friend**, imaginați-vă următoarea analogie: fiecare dintre voi are un set de gânduri și sentimente care sunt **private**,​ **inaccesibile** celorlalți. Totuși, ​ca ființe sociale, avem prieteni cărora alegem să le împărtășim aceste gânduri. Deși prietenii noștri **au acces** la informații personale, acest lucru **nu** înseamnă că oricine poate avea acces la ele. Voi sunteți cei care **dețineți controlul total** asupra a ceea ce **dezvăluiți** și **cui**.</​note>​ 
 + 
 +Pentru a declara o funcție de tip **friend**, folosim cuvântul cheie **friend**. Aceste funcții sunt declarate în interiorul clasei ​și au cuvântul cheie **friend** plasat **înaintea tipului ​de return** al funcției. 
 + 
 +Să urmărim exemplul de cod de mai jos unde am declarat și am implementat o **funcție friend** care afișază datele despre o persoană.
  
 <code cpp> <code cpp>
-#pragma once 
-#include <​cstring>​ 
 #include <​iostream>​ #include <​iostream>​
 +#include <​cstring>​
  
-class Locuinta+class Persoana
 { {
- float pret+ int varsta
- char* ​adresa;+ char* ​nume;
  
 public: public:
  
- Locuinta();​ + Persoana(const ​intvarsta, const char* nume); 
- Locuinta(const ​floatpret, const char* adresa); + ~Persoana();
- Locuinta(const Locuinta&​ locuinta);​ +
- Locuinta&​ operator=(const Locuinta&​ locuinta); +
- ~Locuinta();+
  
- float getPret() const+ friend void afisarePersoana(const Persoana&​ persoana); // functie friend pentru afisarea datelor unei persoane 
- char* getAdresa() const;+};
  
- void setPret(const ​floatpret); +Persoana::​Persoana(const ​intvarsta, ​const char* nume) 
- void setAdresa(const char* adresa);+
 + this->​varsta = varsta;
  
- friend std::ostreamoperator<<(std::ostream&​ outconst Locuinta&​ locuinta); + if (nume != nullptr) 
-};+
 + this->​nume = new char[strlen(nume) + 1]; 
 + strcpy(this->​nume,​ nume); 
 +
 + else 
 +
 + this->​nume = nullptr; 
 +
 +
 + 
 +Persoana::~Persoana() 
 +
 + if (nume != nullptr) 
 +
 + delete[] nume; 
 +
 +
 + 
 +void afisarePersoana(const Persoanapersoana) 
 +
 + std::​cout ​<< ​"​Numele persoanei este: " << persoana.nume << '​\n';​ 
 + std::cout << "​Varsta persoanei este: " << persoana.varsta << "​\n\n";​ 
 +
 + 
 +int main() 
 +
 + Persoana persoana(22"​Andrei"​); 
 + 
 + afisarePersoana(persoana)// apelarea functiei friend este identica cu a unei functii clasice din C/C++ 
 + 
 + return 0; 
 +}
 </​code>​ </​code>​
  
-<​note>​Pe prima linie a **fișierului header** ​în care este definită clasa **Locuinta**, putem observa utilizarea directivei **''#​pragma once''​**. Aceasta este o instrucțiune specifică compilatorului care indică ​faptul că fișierul respectiv trebuie inclus și compilat o **singură dată**, chiar dacă este referit ​în mod repetat în alte fișiere prin intermediul directivelor **''#​include''​**. Astfel, se previn **multiplele incluziuni** ale aceluiași fișier header, care ar putea duce la erori de compilare, ​cum ar fi redefinirea claselor sau funcțiilor. Directiva ​**''#​pragma once''​** este o alternativă modernă și mai simplă la gardienii clasici ai fișierelor header, adică acele secvențe de cod cu **''#​ifndef''​**,​ **''#​define''​** și **''#​endif''​** care au același scop.</​note>​+Î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++.
  
-Dacă voiam să folosim varianta tradițională de scriere ​unui fișier header am fi procedat în maniera următoare.+<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 ​anunț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/C++**.</​note>​
  
-<code cpp> +Vom folosi foarte mult acest tip de funcții după cum vom vedea în cele ce urmează la supraîncărcarea operatorilor limbajului C++.
-#ifndef LOCUINTA_H +
-#define LOCUINTA_H+
  
-#​include ​<cstring>+==== Supraîncărcarea operatorilor ==== 
 + 
 +Î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 observată în codul de mai jos. 
 + 
 +<code cpp>
 #include <​iostream>​ #include <​iostream>​
  
-class Locuinta+class NrComplex
 { {
-    float pret+ double real
-    char* adresa;+ double imaginar;
  
 public: public:
  
-    Locuinta();​ + NrComplex(const ​doublereal = 0.0, const doubleimaginar ​0.0); // constructor cu parametri cu valori implicite
-    Locuinta(const ​floatpret, const char* adresa); +
-    Locuinta(const Locuintalocuinta);​ +
-    Locuinta&​ operator=(const Locuinta&​ locuinta);​ +
-    ~Locuinta();+
  
-    float getPret() const; + double getReal() const; 
-    char* getAdresa() const;+ double getImaginar() const;
  
-    ​void setPret(const ​floatpret); + void setReal(const ​doublereal); 
-    void setAdresa(const ​char* adresa);+ void setImaginar(const ​double& imaginar); 
 +};  
 +</​code>​
  
-    friend std::​ostream&​ operator<<​(std::​ostream&​ out, const Locuinta&​ locuinta);​ +Iar implementările funcțiilor membre sunt vizibile în codul de mai jos.
-};+
  
-#endif // LOCUINTA_H+<code cpp> 
 +#include "​Complex.h"​ 
 + 
 +NrComplex::​NrComplex(const double& real, const double& imaginar) 
 +
 + this->​real = real; 
 + this->​imaginar = imaginar; 
 +
 + 
 +double NrComplex::​getReal() const 
 +
 + return real; 
 +
 + 
 +double NrComplex::​getImaginar() const 
 +
 + return imaginar; 
 +
 + 
 +void NrComplex::​setReal(const double& real) 
 +
 + this->​real = real; 
 +
 + 
 +void NrComplex::​setImaginar(const double& imaginar) 
 +
 + this->​imaginar = imaginar; 
 +}
 </​code>​ </​code>​
  
-=== Relația de tip "is-a" între două clase ===+<​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 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>​
  
-Acest tip de relație ne permite să implementăm ​**moștenirea** între clase. În acest contextcând discutăm despre moștenire, întâlnim următorii termeni esențiali: **clasă părinte (denumită și clasă de bază sau superclasă)** și **clasă derivată (denumită și clasă copil sau subclasă)****Clasa părinte** reprezintă clasa de la care dorim să **preluăm** atribute ​și metode, având posibilitatea să **reutilizăm** codul existent, în timp ce **clasa derivată** **extinde** această ​funcționalitate, **adăugând** noi comportamente și caracteristici.+<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ț**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>​
  
-<note tip>​Atunci când dorim să implementăm **moștenirea** între două clase, este important să respectăm un set clar de reguli. În primul rând, trebuie să stabilim care dintre clase va fi **clasa părinte** și care va fi **clasa copil**. Prin procesul de **moștenire**,​ afirmăm că un **obiect** al **clasei derivate** este, implicit, și un obiect al **clasei părinte**, datorită relației de tip **"​is-a"​** dintre cele două clase. Această relație trebuie să aibă o **coerență logică**, adică orice instanță a **clasei derivate** este automat șo instanță a **superclasei**. De exemplu, omul este un mamifer, iar afirmația că toți oamenii sunt mamifere este corectă. Însă reciproca nu este valabilă, deoarece nu toate mamiferele sunt oameni. Prin urmare, relația de tip **"​is-a"​** se aplică doar de la **clasa copil** către **clasa părinte**, și nu invers. +=== Operatori supraîncărcați ca funcții membre în clasă ===
-</​note>​+
  
-În continuare vom căuta o clasă care poate să extindă și să respecte relația de **"​is-a"​** cu clasa **Locuinta**. Vom propune clasa **Apartament** care respectă regulile descrise mai sus, deoarece orice apartament este o locuință. Clasa **Apartament** are ca și atribute **numarCamere (de tip întreg)** și **numeProprietar (șir de caractere alocat dinamic)**.+În general alegem această variantă de supraîncărcare atunci când vrem să avem acces **direct** la membrii unei claseAstfel, 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 limbajul C++ pentru ​ca o clasă să moștenească o altă clasă se folosește următoarea sintaxă+<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>​
-**''​class NumeClasaDerivata : specificator de acces pentru moștenire NumeClasaParinte''​** și apoi corpul ​clasei ​derivate.+
  
-Să urmărim în cod cum putem face clasa **Apartament** să moștenească clasa **Locuință**.+== 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 **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.
  
 <code cpp> <code cpp>
-#pragma once +#​include ​<​iostream>​
-#​include ​"​Locuinta.h"​+
  
-class Apartament : public Locuinta // clasa Apartament mosteneste clasa Locuinta+class NrComplex
 { {
- int numarCamere+ double real
- char* numeProprietar;+ double imaginar;
  
 public: public:
  
- Apartament();​ + NrComplex(const ​doublereal = 0.0, const doubleimaginar ​0.0);
- Apartament(const ​floatpret, const char* adresa, const intnumarCamere,​ const char* numeProprietar);​ +
- Apartament(const Apartament&​ apartament);​ +
- Apartament&​ operator=(const Apartament&​ apartament);​ +
- ~Apartament();+
  
- int getNumarCamere() const; + double getReal() const; 
- char* getNumeProprietar() const;+ double getImaginar() const;
  
- void setNumarCamere(const ​intnumarCamere); + void setReal(const ​doublereal); 
- void setNumarCamere(const ​char* numeProprietar);+ void setImaginar(const ​double& imaginar);
  
- friend std::​ostream& operator<<(std::​ostream&​ out, const Apartament&​ apartament);+ // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex& operator++(); // forma prefixata 
 + NrComplex operator++(int);​ // forma postfixata
 }; };
 </​code>​ </​code>​
  
-<note warning>​Deși clasa **Apartament** moștenește clasa **Locuinta** din cauza faptului că membrii clasei **Locuinta** sunt marcați din default cu **private** nu vom avea acces la ei în **clasa derivată**.</​note>​+Iar implementările corespunzătoare pentru cele două forme ale operatorului de incrementare le putem vedea mai jos.
  
-=== Specificatorul de acces protected ===+<code cpp> 
 +NrComplex&​ NrComplex::​operator++() 
 +
 + this->​real++;​ 
 + this->​imaginar++;​
  
-Pentru ​putea face câmpurile clasei ​**Locuinta** să fie vizibile în clasa copildar să nu poată fi în continuare accesate din exterior ​de oricine le vom marca cu **protected** după cum urmează în codul de mai jos.+ return *this; 
 +
 + 
 +NrComplex NrComplex::​operator++(int) 
 +
 + NrComplex copie = *this; 
 + 
 + this->​real++;​ 
 + this->​imaginar++;​ 
 + 
 + return copie; 
 +
 +</​code>​ 
 + 
 +<note important>​Se poate observa că la **forma postfixată** avem un parametru de care **nu** ne folosim. Acel parametru este **doar** pentru ​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 cum funcționează conceptul de **overloading**. 
 + 
 +== Supraîncărcarea operatorului ! == 
 + 
 +Acest operator are rolul de a **nega** o **expresie logică**. Deși un număr complex **nu** poate fi negat în sensul unei **negații logice**, putem interpreta negația ca **operație ​de conjugare**. Prin urmare, utilizarea ​**operatorului de negare logică (!)** ar putea fi relevantă pentru clasa **NrComplex**, ​în sensul că am putea implementa acest operator pentru a **returna** conjugatul unui numărul complex. 
 + 
 +În continuare vom declara în clasa **NrComplex** acest operator pentru a realiza ceea ce ne dorim, și anume conjugarea unui număr complex.
  
 <code cpp> <code cpp>
-#pragma once 
-#include <​cstring>​ 
 #include <​iostream>​ #include <​iostream>​
  
-class Locuinta+class NrComplex
 { {
-protected: // specificatorul de acces protected + double real
- + double imaginar;
- float pret+
- char* adresa;+
  
 public: public:
  
- Locuinta();​ + NrComplex(const ​doublereal = 0.0, const doubleimaginar ​0.0);
- Locuinta(const ​floatpret, const char* adresa); +
- Locuinta(const Locuintalocuinta);​ +
- Locuinta&​ operator=(const Locuinta&​ locuinta);​ +
- ~Locuinta();+
  
- float getPret() const; + double getReal() const; 
- char* getAdresa() const;+ double getImaginar() const;
  
- void setPret(const ​floatpret); + void setReal(const ​doublereal); 
- void setAdresa(const ​char* adresa);+ void setImaginar(const ​double& imaginar);
  
- friend std::​ostream& ​operator<<(std::​ostream&​ out, const Locuintalocuinta);+ // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex ​operator!(const; // operatorul de negare logica (in cazul acestei clase va conjuga un numar complex) 
 + 
 + NrComplexoperator++();​ 
 + NrComplex operator++(int);
 }; };
 </​code>​ </​code>​
  
-Astfel acum câmpurile **pret** și **adresa** vor fi vizibile și în clasa derivată, dar vor rămâne **inaccesibile** în funcția **main** sau în orice altă clasă care **nu** moștenește clasa **Locuinta**.+Iar implementarea acestui operator se poate observa mai jos.
  
-=== Implementarea metodelor și a funcțiilor friend în clasa derivată ===+<code cpp> 
 +NrComplex NrComplex::​operator!() const 
 +
 + NrComplex conjugat ​*this;
  
-În continuare vom prezenta modul în care trebuiesc implementate toate funcționalitățile clasei **Apartament** astfel încât relația de **"is-a"** să fie satisfăcută și să reutilizăm codul din clasa **Locuinta**.+ conjugat.imaginar = -conjugat.imaginar;
  
-== Implementarea constructorilor clasei derivate ==+ return conjugat; 
 +
 +</​code>​
  
-Atunci când dorim să construim un obiect de tipul **clasei derivate**trebuie să ținem cont de faptul că, **mai întâi**, trebuie inițializate ​și gestionate toate datele și resursele ​**moștenite** din **clasa părinte**. Constructorul **clasei derivate** va apela constructorul **clasei părinte** pentru a asigura corectitudinea inițializării **componentelor moștenite**. Astfel, acest proces garantează că toate proprietățile părintelui sunt gestionate corespunzător înainte de a se trece la inițializarea specifică **clasei derivate**.+<note warning>​Operatorul de negare logică **nu** trebuie să modifice ​**this-ul**, acesta a fost și motivul pentru care am returnat o **copie modificată** în loc să modifică**direct** obiectul curent.</​note>​
  
-Să urmărim cu atenție mai jos implemetarea constructorului fără parametri pentru ​clasa **Apartament**.+== Supraîncărcarea operatorilor == și != == 
 + 
 +**Operatorul %%==%%** este folosit pentru a testa egalitatea 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**. 
 + 
 +Vom declara după cum urmează aceste două metode în clasa **NrComplex**.
  
 <code cpp> <code cpp>
-Apartament::​Apartament() : Locuinta()+#include <​iostream>​ 
 + 
 +class NrComplex
 { {
- numarCamere = 0+ double real
- numeProprietar = nullptr; + double imaginar;
-+
-</​code>​+
  
-<note important>​Se poate observa că, înainte de a inițializa câmpurile specifice clasei **Apartament**,​ am apelat **constructorul fără parametri** al clasei **Locuință** în **lista de inițializare** a constructorului **clasei derivate**. Astfel, am **reutilizat** codul din **clasa părinte** printr-un simplu apel, asigurând inițializarea corectă a membrilor moșteniți. Este important de menționat că această **listă de inițializare** poate fi utilizată **exclusiv** în cazul **constructorilor**,​ fiind o metodă eficientă de a gestiona inițializarea **clasei părinte**.</​note>​+public:
  
-În continuare vom implementa constructorul cu parametri ​pentru ​clasa copil urmând același principiu ca la constructorul fără parametri.+ NrComplex(const double& real = 0.0, const double& imaginar = 0.0); 
 + 
 + double getReal() const; 
 + double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar);​ 
 + 
 + // supraincarcarea operatorilor functii membre 
 + 
 + NrComplex operator!() const; 
 + 
 + NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​ 
 + 
 + bool operator==(const NrComplex&​ z) const; // operatorul de testare a egalitatii intre doua numere complexe 
 + bool operator!=(const NrComplex&​ z) const; // operatorul de testare a diferentei intre doua numere complexe 
 +}; 
 +</​code>​ 
 + 
 +Iar implementările ​pentru ​cei doi operatori le putem observa în codul de mai jos.
  
 <code cpp> <code cpp>
-Apartament::Apartament(const ​floatpret, const char* adresa, const int& numarCamere,​ const char* numeProprietar) : Locuinta(pret,​ adresa)+bool NrComplex::operator==(const ​NrComplexz) const
 { {
- this->numarCamere ​numarCamere;+ return ​this->real == z.real && this->​imaginar == z.imaginar; 
 +}
  
- if (numeProprietar ​!= nullptr+bool NrComplex::​operator!=(const NrComplex&​ zconst 
-+
- this->numeProprietar ​new char[strlen(numeProprietar) + 1]; + return ​this->real !z.real || this->imaginar !z.imaginar;
- strcpy(this->numeProprietar,​ numeProprietar);​ +
-+
- else +
-+
- this->​numeProprietar ​nullptr; +
- }+
 } }
 </​code>​ </​code>​
  
-Se poate observa din implementarea anterioară că am deschis lista de inițializare pentru acest constructor unde am chemat constructorul cu parametri al clasei părinte (clasa **Locuinta**).+== Supraîncărcarea operatorului += ==
  
-<note warning>​Constructorul cu parametri al **clasei derivate** include în lista sa de argumente și parametrii necesari ​pentru a apela **constructorul corespunzător din clasa părinte**. Acești parametri sunt transmiși în lista de inițializare a constructorului **clasei copil** atunci când este apelat constructorul din **superclasă**,​ facilitând astfel **inițializarea corectă** a **membrilor moșteniți** din **clasa părinte**Acest mecanism permite transmiterea valorilor necesare direct către **clasa părinte**, asigurând o **organizare clară** ​și o **reutilizare eficientă** a codului.</​note>​+Acest operator este o **variantă prescurtată** pentru ​adunarea ​două variabile ​de același tipÎn cazul a două numere complexe vom prezenta în codul de mai jos cum putem declara acest operator în fișierul **header**.
  
-În manieră similară se implementează și constructorul de copiere al clasei derivate.+<code cpp> 
 +#include <​iostream>​ 
 + 
 +class NrComplex 
 +
 + double real; 
 + double imaginar; 
 + 
 +public: 
 + 
 + NrComplex(const double& real = 0.0, const double& imaginar = 0.0); 
 + 
 + double getReal() const; 
 + double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar);​ 
 + 
 + // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex operator!() const; 
 + 
 + NrComplex&​ operator+=(const NrComplex&​ z); // operatorul compus pentru adunarea a doua numere complexe 
 + 
 + NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​ 
 + 
 + bool operator==(const NrComplex&​ z) const;  
 + bool operator!=(const NrComplex&​ z) const; 
 +}; 
 +</​code>​ 
 + 
 +Iar implementarea acestui operator o vom regăsi în secvența de cod următoare.
  
 <code cpp> <code cpp>
-Apartament::Apartament(const ​Apartamentapartament) : Locuinta(apartament)+NrComplex&​ NrComplex::operator+=(const ​NrComplexz)
 { {
- numarCamere ​apartament.numarCamere;+ this->​real +z.real; 
 + this->​imaginar += z.imaginar;
  
- if (apartament.numeProprietar != nullptr) + return *this;
-+
- numeProprietar = new char[strlen(apartament.numeProprietar) + 1]; +
- strcpy(numeProprietar,​ apartament.numeProprietar);​ +
-+
- else +
-+
- numeProprietar = nullptr; +
- }+
 } }
 </​code>​ </​code>​
  
-Astfelam evidențiat ​**reutilizarea codului** prin faptul căîn momentul în care construim un obiect de tipul **clasei derivate**, nu este necesar ​să redefinim sau să duplicăm funcționalitățile și datele din **clasa părinte**. Prin simplul apel al constructorului **clasei părinte** în lista de inițializare a constructorului ​**clasei derivate**, putem păstra claritatea și concizia codului sursă. Aceasta reprezintă un avantaj major al **moștenirii** în **POO**, permițând extinderea funcționalității fără a compromite principiile de reutilizare și organizare.+<note tip>În mod similarputem proceda pentru ​**operatorii -=, /=, *= și %=**. Totuși, este important ​să ne întrebăm dacă aceste operații au o **semnificație logică** în contextul numerelor complexeAtunci când supraîncărcăm operatori, ​**trebuie** să ne asigurăm că operațiile respective ​**au sens logic** în raport cu clasa pentru care le implementăm.</​note>​
  
-== Implementarea destructorului în clasa derivată ​==+==== ====
  
-Destructorul clasei derivate se implementează la fel ca un destructor obișnuitadică vom elibera memoria alocată dinamic ​pentru ​membrii ​care sunt de tip pointer.+<note important>​Nu doar **operatorii unari** sunt supraîncărcați ​ca funcții membre, mai putem supraîncărca ​și operatori precum cel de **indexare ("​[]"​)**,​ operatorul de **apel de funcție ("​()"​)**operatorul **săgeată ("​%%->​%%"​)** și operatorul de **asignare ("​="​)**. Cei din urmă trebuie implementați **exclusiv** ca funcții membre în clasă. De asemenea, și operatorii de comparație pot fi supraîncărcați ca **metode** ale clasei, dar în contextul numerelor complexe aceștia **nu au sens**, deoarece în matematică **nu** există **relație de ordine** în mulțimea numerelor complexe.</​note>​ 
 + 
 +=== Operatori supraîncărcați ca funcții friend === 
 + 
 +În general, alegem să supraîncărcăm operatorii ca **funcții friend** atunci când dorim să permitem ca aceștia să primească ca parametri tipuri de date **diferite** de **clasa** ​pentru care facem supraîncărcarea. Această abordare este utilă mai ales când operatorul trebuie să acceseze **membrii privați** ai clasei, dar implică și **alte** tipuri ​de date în operații. 
 + 
 +== Supraîncărcarea operatorului + == 
 + 
 +Aici putem avea mai multe situații spre exemplu: adunarea a două numere complexe, adunarea dintre un număr complex și unul real și adunarea dintre un număr real și unul complex. Prin urmare avem **trei** situații dintre care două necesită un **alt tip de date** primit ca parametru. Prin urmare vom supraîncărca acest operator ca **funcție friend** punând la dispoziție cele **trei** variante despre care am discutat anterior. 
 + 
 +Să urmărim codul de mai jos unde am pus la dispoziție cele trei forme pentru **operatorul de adunare**.
  
 <code cpp> <code cpp>
-Apartament::​~Apartament()+#include <​iostream>​ 
 + 
 +class NrComplex
 { {
- if (numeProprietar != nullptr) + double real; 
- + double imaginar;
- delete[] numeProprietar; +
-+
-+
-</​code>​+
  
-<note warning>​În destructorul clasei derivate **nu** apelăm destructorul clasei părinte. Acest lucru va fi realizat **automat** de către **compilator** în mod corect fără a fi nevoie de intervenția noastră.</​note>​+public:
  
-== Implementarea operatorului de asignare în clasa derivată ​==+ NrComplex(const double& real 0.0, const double& imaginar ​0.0); 
 + 
 + double getReal() const; 
 + double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar);​ 
 + 
 + // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex operator!() const; 
 + 
 + NrComplex&​ operator+=(const NrComplex&​ z); 
 + 
 + NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​ 
 + 
 + bool operator==(const NrComplex&​ z) const;  
 + bool operator!=(const NrComplex&​ z) const; 
 + 
 + // operatori supraincarcati ca functii friend 
 + 
 + friend NrComplex operator+(const NrComplex&​ z1, const NrComplex&​ z2); // operator pentru adunarea a doua numere complexe 
 + friend NrComplex operator+(const NrComplex&​ z1, const double& numar); // operator pentru adunarea unui numar complex cu un numar real 
 + friend NrComplex operator+(const double& numar, const NrComplex&​ z1); // operator pentru adunarea unui numar real cu un numar complex 
 +}; 
 +</​code>​
  
-La fel ca în cazul constructorilor va trebui să găsim o modalitate prin care **mai întâi** ne ocupăm de atributele **clasei părinte** și pe urmă prelucram datele **clasei copil**. Operatorul de asignare **nefiind** un constructor **nu** are listă de inițializare și va trebui să îl apelăm explicit pe cel din **clasa părinte** ​pentru ​a respecta ordinea pașilor exact la fel ca în cazul constructorilor.+Iar implementările pentru ​cei trei operatori le putem observa ​în codul ce urmează.
  
 <code cpp> <code cpp>
-Apartament&​ Apartament::​operator=(const ​Apartamentapartament)+NrComplex ​operator+(const ​NrComplex&​ z1, const NrComplexz2)
 { {
- if (this == &​apartament) + NrComplex z;
-+
- return *this; +
- }+
  
- this->​Locuinta::​operator=(apartament)// se apeleaza operatorul de asignare din clasa parinte + z.real ​z1.real + z2.real
- /​*(Locuinta&​)(*this) ​apartament// este echivalent cu linia de mai sus doar ca este o alta forma de apel*/+ z.imaginar ​z1.imaginar + z2.imaginar;
  
- numarCamere = apartament.numarCamere;+ return z; 
 +}
  
- if (apartament.numeProprietar != nullptr) +NrComplex operator+(const NrComplex&​ z1const double& numar
-+
- numeProprietar = new char[strlen(apartament.numeProprietar) ​1]; + NrComplex z;
- strcpy(numeProprietarapartament.numeProprietar)+
-+
- else +
-+
- numeProprietar = nullptr; +
- }+
  
- return ​*this;+ z.real = z1.real + numar; 
 + z.imaginar = z1.imaginar;​ 
 + 
 + return ​z; 
 +
 + 
 +NrComplex operator+(const double& numar, const NrComplex&​ z1) 
 +
 + NrComplex z; 
 + 
 + z.real = numar + z1.real; 
 + z.imaginar = z1.imaginar;​ 
 + 
 + return z;
 } }
 </​code>​ </​code>​
  
-== Implementarea operatorului ​<< în clasa derivată ==+<note tip>În manieră similară se poate proceda și pentru ceilalți **operatori aritmetici (-, *, /, %)**. Din nou, **trebuie** să ne punem întrebarea **dacă** operațiile respective au **sens logic** pentru ​clasa unde supraîncărcăm operatorii.</​note>​
  
-Vom prezenta ​în continuare modul de implementare a operatorului ​de afișare ​pentru obiectele ​de tip **Apartament** respectând ​în continuare relația de **"​is-a"​**.+== Supraîncărcarea operatorilor de flux >> și << == 
 + 
 +**Operatorul %%>>​%%** este folosit ​în C++ pentru citirea datelor fie de la tastatură fie dintr-un fișier, în timp ce **operatorul %%<<​%%** este folosit pentru afișarea datelor fie în consolă fie într-un fișier. În general cei doi operatori sunt supraîncărcați ca **funcții friend**, dar mai pot fi întâlniți și ca **funcții globale obișnuite (adică nu sunt marcate ca funcții friend)** unde operațiile ​de citire și afișare ​se realizează cu ajutorul **metodelor accesor** ​de tip **get** și **set**. 
 + 
 +Să urmărim cu atenție modul în care declarăm acesți operatori ca **funcții friend** în clasa **NrComplex**.
  
 <code cpp> <code cpp>
-std::​ostream&​ operator<​<(std::​ostream&​ out, const Apartament&​ apartament)+#​include ​<iostream>​ 
 +using namespace ​std
 + 
 +class NrComplex
 { {
- operator<<​(out,​ (Locuinta&​)apartament)// chemam operatorul << din clasa parinte+ double real; 
 + double imaginar;
  
- out << "​Numarul de camere din apartament este" << apartament.numarCamere << " ron\n";​+public:
  
- if (apartament.numeProprietar !nullptr) + NrComplex(const double& real = 0.0, const double& imaginar ​0.0);
-+
- out << "​Numele proprietarului este: " << apartament.numeProprietar << "​\n";​ +
-+
- else +
-+
- out << "​Numele proprietarului este: N/A\n"; +
- }+
  
- return ​out; + double getReal() const; 
-}+ double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar);​ 
 + 
 + // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex operator!() const; 
 + 
 + NrComplex&​ operator+=(const NrComplex&​ z); 
 + 
 + NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​ 
 + 
 + bool operator==(const NrComplex&​ z) const;  
 + bool operator!=(const NrComplex&​ z) const; 
 + 
 + // operatori supraincarcati ca functii friend 
 + 
 + friend NrComplex operator+(const NrComplex&​ z1, const NrComplex&​ z2); 
 + friend NrComplex operator+(const NrComplex&​ z1, const double& numar); 
 + friend NrComplex operator+(const double& numar, const NrComplex&​ z1); 
 + 
 + friend istream&​ operator>>​(istream&​ in, NrComplex&​ z); // operator pentru citirea unui numar complex 
 + friend ostream&​ operator<<​(ostream& ​out, const NrComplex&​ z)// operator pentru afisarea unui numar complex 
 +};
 </​code>​ </​code>​
  
-<​note ​warning>Funcțiile **friend** dintr-o clasă **nu** se moștenesc automat de tre **clasa derivată**, motiv pentru care trebuie să apelăm explicit ​**operatorul %%<<​%%** definit în **clasa ​de bază**. Pentru a înțelege mai bine acest comportament,​ putem face următoarea analogie: prietenii părinților voștri ​**nu sunt neapărat** și prietenii voștriRelația ​de prietenie este specifică ​**doar** între părinții voștri ​și acele persoane, iar aceasta ​**nu se extinde automat** asupra voastră. La fel, funcțiile ​**friend** sunt prietene ale **clasei părinte**, dar **nu** devin prietene implicit și pentru ​**clasa derivată**.</​note>​+<​note>​Se poate observa ​că cele două **funcții friend** primesc câte un parametru special ​de tip **stream**. Tipurile ​de date **istream** și **ostream** anunță compilatorul că vrem să lucrăm cu fluxuri de date. **Istream** vine de la **input stream (flux de intrare)** în timp ce **ostream** vine de la **output stream (flux de ieșire)**. 
 +</​note>​
  
-==== ====+Să urmărim implementarea celor doi operatori de flux în exemplul de cod de mai jos.
  
-Acum că am înțeles conceptul de **moștenire** între două clasevom putea avansa către implementarea unor ierarhii mai complexe începând cu următorul laborator. Moștenirea ne permite să construim structuri **ierarhice**,​ în care clasele pot extinde și reutiliza funcționalități din **clasele părinte**. Astfel, vom fi capabili să dezvoltăm sisteme mai robuste, eficiente și ușor de întreținut,​ în care fiecare clasă va adăuga comportamente și atribute specifice, păstrând în același timp funcționalitatea de bază moștenită. Aceste ierarhii de clase vor facilita gestionarea mai bună codului și îmbunătățirea scalabilității aplicațiilor noastre.+<code cpp> 
 +istream&​ operator>>​(istream&​ inNrComplex&​ z) 
 +
 + std::cout << "​Introduceti partea reala numarului complex: "; 
 + in >> z.real;
  
-==== Concluzii ====+ std::cout << "​Introduceti partea imaginara a numarului complex: "; 
 + in >> z.imaginar;
  
-În cadrul acestui laborator, am învățat și aprofundat conceptul de **moștenire** și am văzut cum poate fi implementată între **două clase**. Am înțeles că **moștenirea** este o metodă esențială pentru a **reutiliza** și **extinde** codul existent, oferind un cadru flexibil și scalabil pentru dezvoltarea aplicațiilor **OOP**. Prin utilizarea moștenirii,​ o **clasă derivată** poate prelua proprietățile unei **clase părinte**, oferind astfel posibilitatea de a adăuga sau modifica funcționalități specifice.+ return in; 
 +}
  
-Un aspect important pe care l-am discutat ​este faptul că **funcțiile friend nu se moștenesc**Aceste funcții, deși pot accesa membri privați sau protejați ai unei clase, **nu** sunt automat apelate în **clasa derivată**. Pentru ​înțelege acest comportament,​ am făcut o analogie simplăprietenii părinților voștri nu sunt în mod automat și prietenii voștri direcți. Astfel, în cazul în care dorim să accesăm funcționalitățile unei funcții friend dintr-o **clasă părinte**, va trebui **să o apelăm explicit** în **clasa derivată**.+ostream&​ operator<<​(ostream&​ out, const NrComplex&​ z) 
 +
 + out << "​Partea reala a numarului complex ​este: " << z.real << '​\n';​ 
 + out << "​Partea imaginara ​numarului complex este" << z.imaginar << "​\n\n";​
  
-De asemenea, am explorat rolul specificatorului de acces **protected**,​ care permite membrilor clasei să fie accesibili în cadrul **claselor derivate**, dar să rămână inaccesibili din exterior. Această abordare oferă un echilibru între **încapsulare** și **moștenire**,​ protejând datele interne ale **clasei părinte**, dar permițând totuși **clasei copil** să le utilizeze.+ return out; 
 +
 +</​code>​
  
-Un alt concept esențial a fost utilizarea ​**listei de inițializare a constructorului** în clasele derivate. În momentul în care instanțiem un obiect din clasa derivată, trebuie să avem grijă să inițializăm corect și **membrii ​clasei părinte**. Aceasta se realizează prin apelarea ​**explicită** **constructorului părinte** în **lista de inițializare a constructorului clasei derivate**. Am subliniat importanțacestui mecanism, deoarece ​**doar** constructorii pot fi apelați în această manieră.+==== ==== 
 + 
 +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 următorilor operatori:​ 
 +  * de rezoluție **"::"​** 
 +  * de acces la membrii ​unei clase/​structuri ​**"."** 
 +  ​condițional/​ternar ​**"?:"​** 
 +  ​* **sizeof** (returnează dimensiunea în bytes unui tip de date) 
 +  ​* **typeid** (folosit pentru a obține informații despre tipul dinamic al unui obiect la run time) 
 +  * **alignof** (similar cu operatorul **sizeof**) 
 +</​note>​ 
 + 
 +==== Concluzii ====
  
-În pluspentru a accesa metode sau funcții din **clasa părinte** care nu sunt constructori,​ trebuie ​să apelăm **explicit** funcția dorită folosind sintaxa: ​**''​numeClasăPărinte::​numeMetodă()''​**. Acest apel este necesar pentru a ne asigura că execută**corect** comportamentul definit ​în **clasa părinte** asupra obiectelor din **clasa fiu**.+În cadrul acestui laboratoram 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.
  
-Prin toate aceste concepte și tehniciam făcut un pas important în **utilizarea eficientă** a **moștenirii** în limbajul C++șsuntem pregătiți să explorăm ierarhii mai complexe de clase în laboratoarele viitoare.+Am înțelesde 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 claseicum ar fi **operatorii unari** sau **operatorul de asignare**, sunt adesea implementaț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**.
poo-is-ab/laboratoare/05.1731312867.txt.gz · Last modified: 2024/11/11 10:14 by razvan.cristea0106
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0