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 16:42]
razvan.cristea0106 [Moștenirea între două clase]
poo-is-ab:laboratoare:05 [2025/01/19 22:29] (current)
razvan.cristea0106
Line 17: Line 17:
  
 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 existente. Astfel, **moștenirea** facilitează **extensibilitatea** și **întreținerea** codului, reducând duplicarea și oferind un mod eficient de a gestiona complexitatea în proiecte de mari dimensiuni. 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 existente. Astfel, **moștenirea** facilitează **extensibilitatea** și **întreținerea** codului, reducând duplicarea și oferind un mod eficient de a gestiona complexitatea în proiecte de mari dimensiuni.
- 
- 
- 
 ==== Moștenirea între două clase ==== ==== Moștenirea între două clase ====
  
Line 52: Line 49:
  friend std::​ostream&​ operator<<​(std::​ostream&​ out, const Locuinta&​ locuinta);  friend std::​ostream&​ operator<<​(std::​ostream&​ out, const Locuinta&​ locuinta);
 }; };
-</​code>​ 
- 
-Iar implementările pentru funcțiile membre și cea friend le putem observa în codul de mai jos. 
- 
-<code cpp> 
-#include "​Locuinta.h"​ 
- 
-Locuinta::​Locuinta() 
-{ 
- pret = 0.0f; 
- adresa = nullptr; 
-} 
- 
-Locuinta::​Locuinta(const float& pret, const char* adresa) 
-{ 
- this->​pret = pret; 
- 
- if (adresa != nullptr) 
- { 
- this->​adresa = new char[strlen(adresa) + 1]; 
- strcpy(this->​adresa,​ adresa); 
- } 
- else 
- { 
- this->​adresa = nullptr; 
- } 
-} 
- 
-Locuinta::​Locuinta(const Locuinta&​ locuinta) 
-{ 
- pret = locuinta.pret;​ 
- 
- if (locuinta.adresa != nullptr) 
- { 
- adresa = new char[strlen(locuinta.adresa) + 1]; 
- strcpy(adresa,​ locuinta.adresa);​ 
- } 
- else 
- { 
- adresa = nullptr; 
- } 
-} 
- 
-Locuinta&​ Locuinta::​operator=(const Locuinta&​ locuinta) 
-{ 
- if (this == &​locuinta) 
- { 
- return *this; 
- } 
- 
- if (adresa != nullptr) 
- { 
- delete[] adresa; 
- } 
- 
- pret = locuinta.pret;​ 
- 
- if (locuinta.adresa != nullptr) 
- { 
- adresa = new char[strlen(locuinta.adresa) + 1]; 
- strcpy(adresa,​ locuinta.adresa);​ 
- } 
- else 
- { 
- adresa = nullptr; 
- } 
- 
- return *this; 
-} 
- 
-Locuinta::​~Locuinta() 
-{ 
- if (adresa != nullptr) 
- { 
- delete[] adresa; 
- } 
-} 
- 
-float Locuinta::​getPret() const 
-{ 
- return pret; 
-} 
- 
-char* Locuinta::​getAdresa() const 
-{ 
- return adresa; 
-} 
- 
-void Locuinta::​setPret(const float& pret) 
-{ 
- if (pret <= 0.0f) 
- { 
- return; 
- } 
- 
- this->​pret = pret; 
-} 
- 
-void Locuinta::​setAdresa(const char* adresa) 
-{ 
- if (adresa == nullptr) 
- { 
- return; 
- } 
- 
- if (this->​adresa != nullptr) 
- { 
- delete[] this->​adresa;​ 
- } 
- 
- this->​adresa = new char[strlen(adresa) + 1]; 
- strcpy(this->​adresa,​ adresa); 
-} 
- 
-std::​ostream&​ operator<<​(std::​ostream&​ out, const Locuinta&​ locuinta) 
-{ 
- out << "​Pretul locuintei este: " << locuinta.pret << " ron\n";​ 
- 
- if (locuinta.adresa != nullptr) 
- { 
- out << "​Adresa locuintei este: " << locuinta.adresa << "​\n\n";​ 
- } 
- else 
- { 
- out << "​Adresa locuintei este: inexistenta\n\n";​ 
- } 
- 
- return out; 
-} 
 </​code>​ </​code>​
  
Line 219: Line 87:
 </​code>​ </​code>​
  
-=== Relația de "​is-a"​ între două clase ===+=== Relația de tip "​is-a"​ între două clase ===
  
 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. 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.
Line 300: Line 168:
 === Implementarea metodelor și a funcțiilor friend în clasa derivată === === Implementarea metodelor și a funcțiilor friend în clasa derivată ===
  
-Î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**.+Î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**.
  
 == Implementarea constructorilor clasei derivate == == Implementarea constructorilor clasei derivate ==
Line 339: Line 207:
 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**). 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**).
  
-<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>​+<note warning>​Constructorul cu parametri ​din **clasa derivată** 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>​
  
-În manieră ​simlară se implementează și constructorul de copiere al clasei derivate.+În manieră ​similară se implementează și constructorul de copiere al clasei derivate.
  
 <code cpp> <code cpp>
Line 378: Line 246:
 <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>​ <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>​
  
 +== Implementarea operatorului de asignare în clasa derivată ==
 +
 +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.
 +
 +<code cpp>
 +Apartament&​ Apartament::​operator=(const Apartament&​ apartament)
 +{
 + if (this == &​apartament)
 + {
 + return *this;
 + }
 +
 + this->​Locuinta::​operator=(apartament);​ // se apeleaza operatorul de asignare din clasa parinte
 + /​*(Locuinta&​)(*this) = apartament; // este echivalent cu linia de mai sus doar ca este o alta forma de apel*/
 +
 + numarCamere = apartament.numarCamere;​
 +
 + if (apartament.numeProprietar != nullptr)
 + {
 + numeProprietar = new char[strlen(apartament.numeProprietar) + 1];
 + strcpy(numeProprietar,​ apartament.numeProprietar);​
 + }
 + else
 + {
 + numeProprietar = nullptr;
 + }
 +
 + return *this;
 +}
 +</​code>​
 +
 +== Implementarea operatorului << în clasa derivată ==
 +
 +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"​**.
 +
 +<code cpp>
 +std::​ostream&​ operator<<​(std::​ostream&​ out, const Apartament&​ apartament)
 +{
 + operator<<​(out,​ (Locuinta&​)apartament);​ // chemam operatorul << din clasa parinte
 +
 + out << "​Numarul de camere din apartament este: " << apartament.numarCamere << " ron\n";​
 +
 + if (apartament.numeProprietar != nullptr)
 + {
 + out << "​Numele proprietarului este: " << apartament.numeProprietar << "​\n";​
 + }
 + else
 + {
 + out << "​Numele proprietarului este: N/​A\n";​
 + }
 +
 + return out;
 +}
 +</​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.
 +
 +==== Concluzii ====
 +
 +Î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.
 +
 +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 plus, pentru 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ăm **corect** comportamentul definit în **clasa părinte** asupra obiectelor din **clasa fiu**.
 +
 +Prin toate aceste concepte și tehnici, am făcut un pas important în **utilizarea eficientă** a **moștenirii** în limbajul C++, și suntem pregătiți să explorăm ierarhii mai complexe de clase în laboratoarele viitoare.
poo-is-ab/laboratoare/05.1728826944.txt.gz · Last modified: 2024/10/13 16:42 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