Differences

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

Link to this comparison view

poo-is-ab:laboratoare:05 [2024/10/13 18:26]
razvan.cristea0106 [Concluzii]
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.
  
 +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.
  
 +==== Operatorii limbajului C++ ====
  
-==== Moștenirea între două clase ====+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.
  
-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 existenteScopul principal al **moștenirii** este de a promova ​**reutilizarea codului** și de a permite o **extensie** naturală a funcționalităților inițialeastfel încât să se creeze o structură mai **flexibilă** și mai **ușor de întreținut** în cadrul aplicațiilor.+**Operatori Unari**: acești operatori au nevoie de **un singur** operand pentru a-și îndeplini ​funcțiaEi sunt folosiți în mod frecvent pentru operații simple precum negarea, incrementarea ​sau decrementarea valorii operanduluiExemple comune includ ​**++ (incrementare)****%%--%% (decrementare)**, **! (negare logică)** și **- (negare aritmetică)**.
  
-Înainte de a explica moștenirea între două clase vom face o scurtă recapitulare a noțiunilor deja învățate în cadrul laboratoarelor anterioare. Pentru acest laborator propunem clasa **Locuinta** care are ca și membri ​**pret (de tip float)** și **adresa (șir de caractere alocat dinamic)**.+**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 (==, !=, <, >)**.
  
-<code cpp> +**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.
-#pragma once +
-#include <​cstring>​ +
-#include <​iostream>​+
  
-class Locuinta +Î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.
-+
- float pret; +
- char* adresa;+
  
-public:+^        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 ​        |
  
- Locuinta();​ +<note important>​Pentru operatorii din tabelul de mai sus am ales această ordine pentru a 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>​
- Locuinta(const float& pretconst charadresa); +
- Locuinta(const Locuinta&​ locuinta);​ +
- Locuinta&​ operator=(const Locuinta&​ locuinta);​ +
- ~Locuinta();​+
  
- float getPret() const; +==== Funcții friend ====
- char* getAdresa() const;+
  
- void setPret(const float& pret); +Î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. ​
- void setAdresa(const charadresa);+
  
- friend ​std::​ostream&​ operator<<​(std::​ostream&​ outconst Locuinta&​ locuinta);​ +<note tip>​Pentru a înțelege mai bine rolul funcțiilor **friend**, imaginați-vă următoarea analogiefiecare 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 personaleacest 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>
-}; +
-</code>+
  
-Iar implementările pentru ​funcțiile membre ​și cea friend ​le putem observa în codul de mai jos.+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>
-#​include ​"​Locuinta.h"​+#​include ​<​iostream>​ 
 +#include <​cstring>​
  
-Locuinta::​Locuinta()+class Persoana
 { {
- pret = 0.0f+ int varsta
- adresa = nullptr; + char* nume;
-}+
  
-Locuinta::Locuinta(const ​floatpret, const char* adresa)+public: 
 + 
 + Persoana(const int& varsta, const char* nume); 
 + ~Persoana();​ 
 + 
 + friend void afisarePersoana(const Persoana&​ persoana); // functie friend pentru afisarea datelor unei persoane 
 +}; 
 + 
 +Persoana::Persoana(const ​intvarsta, const char* nume)
 { {
- this->pret pret;+ this->varsta ​varsta;
  
- if (adresa ​!= nullptr)+ if (nume != nullptr)
  {  {
- this->adresa ​= new char[strlen(adresa) + 1]; + this->nume = new char[strlen(nume) + 1]; 
- strcpy(this->​adresaadresa);+ strcpy(this->​numenume);
  }  }
  else  else
  {  {
- this->adresa ​= nullptr;+ this->nume = nullptr;
  }  }
 } }
  
-Locuinta::Locuinta(const Locuinta&​ locuinta)+Persoana::~Persoana()
 { {
- pret = locuinta.pret;​ + if (nume != nullptr)
- +
- if (locuinta.adresa ​!= nullptr)+
  {  {
- adresa = new char[strlen(locuinta.adresa) + 1]+ delete[] nume;
- strcpy(adresa,​ locuinta.adresa);​ +
-+
- else +
-+
- adresa = nullptr;+
  }  }
 } }
  
-Locuinta&​ Locuinta::​operator=(const ​Locuintalocuinta)+void afisarePersoana(const ​Persoanapersoana)
 { {
- if (this == &​locuinta) + std::cout << "​Numele persoanei este: " << persoana.nume << '​\n';​ 
- + std::cout << "​Varsta persoanei este: " << persoana.varsta << "​\n\n"​
- return *this+}
- }+
  
- if (adresa != nullptr+int main() 
-+
- delete[] adresa; + Persoana persoana(22,​ "​Andrei"​);
- }+
  
- pret = locuinta.pret;+ afisarePersoana(persoana)// apelarea functiei friend este identica cu a unei functii clasice din C/C++
  
- if (locuinta.adresa != nullptr) + return ​0;
-+
- adresa = new char[strlen(locuinta.adresa) + 1]; +
- strcpy(adresa,​ locuinta.adresa);​ +
-+
- else +
-+
- adresa = nullptr; +
-+
- +
- return ​*this;+
 } }
 +</​code>​
  
-Locuinta::​~Locuinta()+Î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 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>​ 
 + 
 +Vom folosi foarte mult acest tip de funcții după cum vom vedea în cele ce urmează la supraîncărcarea operatorilor limbajului C++. 
 + 
 +==== 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>​ 
 + 
 +class NrComplex
 { {
- if (adresa !nullptr+ double real; 
- { + double imaginar; 
- delete[] adresa+ 
- }+public: 
 + 
 + NrComplex(const double& real 0.0, const double& imaginar = 0.0); // constructor cu parametri cu valori implicite 
 + 
 + double getReal() const; 
 + double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar)
 +};  
 +</​code>​ 
 + 
 +Iar implementările funcțiilor membre sunt vizibile în codul de mai jos. 
 + 
 +<code cpp> 
 +#include "​Complex.h"​ 
 + 
 +NrComplex::​NrComplex(const double& real, const double& imaginar) 
 +
 + this->​real = real; 
 + this->​imaginar = imaginar;
 } }
  
-float Locuinta::getPret() const+double NrComplex::getReal() const
 { {
- return ​pret;+ return ​real;
 } }
  
-char* Locuinta::getAdresa() const+double NrComplex::getImaginar() const
 { {
- return ​adresa;+ return ​imaginar;
 } }
  
-void Locuinta::setPret(const ​floatpret)+void NrComplex::setReal(const ​doublereal)
 { {
- if (pret <0.0f) + this->​real ​real
-+}
- return+
- }+
  
- this->pret pret;+void NrComplex::​setImaginar(const double& imaginar) 
 +
 + this->imaginar ​imaginar;
 } }
 +</​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 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 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ă ===
 +
 +Î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 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 ++ ==
 +
 +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>
 +#include <​iostream>​
  
-void Locuinta::​setAdresa(const char* adresa)+class NrComplex
 { {
- if (adresa == nullptr) + double real; 
- + double imaginar;
- return; +
- }+
  
- if (this->​adresa != nullptr) +public:
-+
- delete[] this->​adresa;​ +
- }+
  
- this->​adresa ​new char[strlen(adresa) + 1]+ NrComplex(const double& real 0.0, const double& imaginar = 0.0); 
- strcpy(this->adresa, adresa);+ 
 + double getReal() const; 
 + double getImaginar() const; 
 + 
 + void setReal(const double& real); 
 + void setImaginar(const double& imaginar);​ 
 + 
 + // supraincarcarea operatorilor ca functii membre 
 + 
 + NrComplex&​ operator++()// forma prefixata 
 + NrComplex operator++(int); // forma postfixata 
 +}; 
 +</​code>​ 
 + 
 +Iar implementările corespunzătoare pentru cele două forme ale operatorului de incrementare le putem vedea mai jos. 
 + 
 +<code cpp> 
 +NrComplex&​ NrComplex::​operator++() 
 +
 + this->real++; 
 + this->​imaginar++;​ 
 + 
 + return *this;
 } }
  
-std::ostream& ​operator<<(std::​ostream&​ out, const Locuinta&​ locuinta)+NrComplex NrComplex::operator++(int)
 { {
- out << "​Pretul locuintei este: " << locuinta.pret << " ron\n";+ NrComplex copie = *this;
  
- if (locuinta.adresa != nullptr) + this->​real++
-+ this->​imaginar++;
- out << "​Adresa locuintei este: " << locuinta.adresa << "​\n"​+
- +
- else +
-+
- out << "​Adresa locuintei este: inexistenta\n"​; +
- }+
  
- return ​out;+ return ​copie;
 } }
 </​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 headeradică acele secvențde cod cu **''#​ifndef''​**,​ **''#​define''​** și **''#​endif''​** care au același scop.</​note>​+<​note ​important>Se poate observa că la **forma postfixată** avem un parametru de care **nu** ne folosimAcel parametru este **doar** pentru a asigura ​**polimorfismul**, compilatorul făcând distincția între cele două variante ​de operator de incrementare.</​note>​
  
-Dacă voiam să folosim varianta tradițională de scriere ​unui fișier header am fi procedat ​în maniera următoare.+Pentru **operatorul de decrementare** se aplică aceleași **exact** aceeași pași, încercați ​să îl implementați voi pentru ​putea înțelege mai bine cum funcționează conceptul de **overloading**.
  
-<code cpp> +== Supraîncărcarea operatorului ! ==
-#ifndef LOCUINTA_H +
-#define LOCUINTA_H+
  
-#​include ​<cstring>+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>
 #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);
-    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 Locuinta&​ locuinta);​ + // supraincarcarea operatorilor ca functii membre
-};+
  
-#​endif ​// LOCUINTA_H+ NrComplex operator!() const; ​// operatorul de negare logica (in cazul acestei clase va conjuga un numar complex) 
 + 
 + NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​ 
 +};
 </​code>​ </​code>​
  
-=== Relația de "​is-a"​ între două clase ===+Iar implementarea acestui operator se poate observa mai jos.
  
-Acest tip de relație ne permite să implementăm **moștenirea** între clase. În acest context, câ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.+<code cpp> 
 +NrComplex NrComplex::operator!() const 
 +
 + NrComplex conjugat = *this;
  
-<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 și 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. + conjugat.imaginar = -conjugat.imaginar;
-</​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)**.+ return conjugat; 
 +
 +</​code>​
  
-În limbajul C++ pentru ca o clasă să moștenească ​altă clasă se folosește următoarea sintaxă: +<note warning>​Operatorul de negare logică **nu** trebuie ​să modifice **this-ul**,​ acesta a fost și motivul pentru care am returnat ​**copie modificată** în loc să modifică**direct** obiectul curent.</​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 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>
-#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::​ostreamout, const Apartamentapartament);+ // supraincarcarea operatorilor functii membre 
 + 
 + NrComplex operator!() const; 
 + 
 + NrComplex& operator++(); 
 + NrComplex operator++(int);​ 
 + 
 + bool operator==(const NrComplexz) const; // operatorul de testare a egalitatii intre doua numere complexe 
 + bool operator!=(const NrComplexzconst// operatorul de testare a diferentei intre doua numere complexe
 }; };
 </​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 pentru cei doi operatori le putem observa ​în codul de mai jos.
  
-=== Specificatorul de acces protected ​===+<code cpp> 
 +bool NrComplex::​operator==(const NrComplex&​ z) const 
 +
 + return this->​real ​== z.real && this->​imaginar ​== z.imaginar;​ 
 +}
  
-Pentru a putea face câmpurile clasei **Locuinta** să fie vizibile în clasa copil, dar să nu poată fi în continuare accesate din exterior de oricine le vom marca cu **protected** după cum urmează în codul de mai jos.+bool NrComplex::​operator!=(const NrComplex&​ z) const 
 +
 + return this->​real != z.real || this->​imaginar != z.imaginar;​ 
 +
 +</​code>​ 
 + 
 +== Supraîncărcarea operatorului += == 
 + 
 +Acest operator este o **variantă prescurtată** pentru adunarea a 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**.
  
 <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 Locuinta&​ locuinta);​ + // supraincarcarea operatorilor ca functii membre
-}; +
-</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**.+ NrComplex operator!() const;
  
-=== Implementarea metodelor și funcțiilor friend în clasa derivată ===+ NrComplex&​ operator+=(const NrComplex&​ z); // operatorul compus pentru adunarea ​doua numere complexe
  
-În continuare vom prezenta modul în care trebuiesc implementate toate funcționalitățile clasei **Apartament** astfel încât realția de **"​is-a"​** să fie satisfăcută și să reutilizăm codul din clasa **Locuinta**.+ NrComplex&​ operator++();​ 
 + NrComplex operator++(int);​
  
-== Implementarea constructorilor clasei derivate =+ bool operator==(const NrComplex&​ z) const;  
- + bool operator!=(const NrComplex&​ z) const; 
-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**.+}; 
 +</​code>​
  
-Să urmărim cu atenție mai jos implemetarea constructorului fără parametri pentru clasa **Apartament**.+Iar implementarea acestui operator o vom regăsi în secvența de cod următoare.
  
 <code cpp> <code cpp>
-Apartament::Apartament() : Locuinta()+NrComplex&​ NrComplex::operator+=(const NrComplex&​ z)
 { {
- numarCamere ​0+ this->​real +z.real
- numeProprietar ​nullptr;+ this->​imaginar +z.imaginar;​ 
 + 
 + return *this;
 } }
 </​code>​ </​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**Astfelam **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>​+<​note ​tip>În mod similarputem proceda pentru ​**operatorii -=, /=, *= și %=**. Totușieste 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ăcă operațiile respective ​**au sens logic** în raport cu clasa pentru care le implementăm.</​note>​
  
-În continuare vom implementa ​constructorul cu parametri ​pentru ​clasa copil urmând același principiu ​ca la constructorul fără parametri.+==== ==== 
 + 
 +<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(const float& pret, const char* adresa, const int& numarCamere,​ const char* numeProprietar) : Locuinta(pret,​ adresa)+#include <​iostream>​ 
 + 
 +class NrComplex
 { {
- this->​numarCamere = numarCamere;+ double real; 
 + double imaginar;
  
- if (numeProprietar != nullptr) +public:
-+
- this->​numeProprietar = new char[strlen(numeProprietar) + 1]; +
- strcpy(this->​numeProprietar,​ numeProprietar);​ +
-+
- else +
-+
- this->​numeProprietar = nullptr; +
-+
-+
-</​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**).+ NrComplex(const double& real = 0.0, const double& imaginar = 0.0);
  
-<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>​+ double getReal() const; 
 + double getImaginar() const;
  
-În manieră simlară se implementează și constructorul de copiere al clasei derivate.+ 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>​ 
 + 
 +Iar implementările pentru cei trei operatori le putem observa în codul ce urmează.
  
 <code cpp> <code cpp>
-Apartament::​Apartament(const ​Apartamentapartament) : Locuinta(apartament)+NrComplex operator+(const ​NrComplexz1, const NrComplex&​ z2)
 { {
- numarCamere = apartament.numarCamere;+ NrComplex z;
  
- if (apartament.numeProprietar !nullptr) + z.real z1.real z2.real
-+ z.imaginar = z1.imaginar + z2.imaginar
- numeProprietar = new char[strlen(apartament.numeProprietar) ​1]+ 
- strcpy(numeProprietar,​ apartament.numeProprietar)+ return z;
- } +
- else +
-+
- numeProprietar = nullptr; +
- }+
 } }
-</​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.+NrComplex operator+(const NrComplex&​ z1const double& numar) 
 +
 + NrComplex z;
  
-== Implementarea destructorului în clasa derivată ==+ z.real ​z1.real + numar; 
 + z.imaginar ​z1.imaginar;​
  
-Destructorul clasei derivate se implementează la fel ca un destructor obișnuit, adică vom elibera memoria alocată dinamic pentru membrii care sunt de tip pointer.+ return z; 
 +}
  
-<code cpp> +NrComplex operator+(const double& numar, const NrComplex&​ z1)
-Apartament::​~Apartament()+
 { {
- if (numeProprietar != nullptr) + NrComplex z; 
- { + 
- delete[] numeProprietar+ z.real = numar + z1.real
- }+ z.imaginar = z1.imaginar;​ 
 + 
 + return z;
 } }
 </​code>​ </​code>​
  
-<​note ​warning>​În ​destructorul clasei derivate ​**nu** apelăm destructorul clasei părinteAcest lucru va fi realizat ​**automat** de către **compilator** în mod corect fără a fi nevoie de intervenția noastră.</​note>​+<​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>​
  
-== Implementarea operatorului de asignare în clasa derivată ==+== Supraîncărcarea operatorilor de flux >> și << ​==
  
-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.+**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>
-Apartament&​ Apartament::​operator=(const Apartament&​ apartament)+#include <​iostream>​ 
 +using namespace std; 
 + 
 +class NrComplex
 { {
- if (this == &​apartament) + double real; 
- + double imaginar;
- return *this; +
- }+
  
- this->​Locuinta::​operator=(apartament);​ // se apeleaza operatorul de asignare din clasa parinte +public:
- /​*(Locuinta&​)(*this) = apartament; // este echivalent cu linia de mai sus doar ca este o alta forma de apel*/+
  
- numarCamere ​apartament.numarCamere;+ NrComplex(const double& real 0.0, const double& imaginar = 0.0);
  
- if (apartament.numeProprietar != nullptr) + double getReal() const
-+ double getImaginar() const;
- numeProprietar = new char[strlen(apartament.numeProprietar) + 1]+
- strcpy(numeProprietar,​ apartament.numeProprietar); +
-+
- else +
-+
- numeProprietar = nullptr; +
- }+
  
- return *this+ 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>​
  
-== Implementarea operatorului ​<< în clasa derivată ==+<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>​
  
-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"​**.+Să urmărim implementarea celor doi operatori de flux în exemplul ​de cod de mai jos.
  
 <code cpp> <code cpp>
-std::​ostream& operator<<(std::​ostreamoutconst Apartamentapartament)+istream& operator>>(istreaminNrComplexz)
 { {
- operator<<(out, (Locuinta&​)apartament)// chemam operatorul << din clasa parinte+ std::​cout ​<< ​"​Introduceti partea reala a numarului complex: "; 
 + in >> z.real;
  
- out << "Numarul de camere din apartament este: " ​<< apartament.numarCamere << " ron\n";+ std::​cout ​<< "Introduceti partea imaginara a numarului complex: "
 + in >> z.imaginar;
  
- if (apartament.numeProprietar != nullptr+ return in; 
-+
- out << "Adresa locuintei ​este: " << ​apartament.numeProprietar ​<< ​"\n"+ 
- +ostream&​ operator<<​(ostream&​ out, const NrComplex&​ z
- else +
-+ out << "Partea reala a numarului complex ​este: " << ​z.real << ​'\n'
- out << "Numele proprietarului ​este: N/A\n"; + out << "Partea imaginara a numarului complex ​este: " << z.imaginar << "\n\n";
- }+
  
  return out;  return out;
 } }
 </​code>​ </​code>​
- 
-<note warning>​Funcțiile **friend** dintr-o clasă **nu** se moștenesc automat de că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ștri. Relaț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>​ 
  
 ==== ==== ==== ====
  
-Acum că am înțeles conceptul de **moștenire** între două clase, vom 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ă a codului și îmbunătățirea scalabilității aplicațiilor noastre.+Codul complet ​cu implementările operatorilor prezentați pentru clasa **NrComplex** poate fi descărcat de {{:​poo-is-ab:​laboratoare:​complex_overloading.zip|aici}}.
  
-==== Concluzii ====+<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 a 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>​
  
-Î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. +==== Concluzii ====
- +
-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 a î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ă**. +
- +
-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. +
- +
-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ă** a **constructorului părinte** în **lista de inițializare a constructorului clasei derivate**. Am subliniat importanța acestui mecanism, deoarece **doar** constructorii pot fi apelați în această manieră.+
  
-Î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.1728833180.txt.gz · Last modified: 2024/10/13 18:26 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