This shows you the differences between two versions of the page.
|
poo-is-ab:laboratoare:06 [2025/09/23 21:25] razvan.cristea0106 |
poo-is-ab:laboratoare:06 [2025/11/03 21:02] (current) razvan.cristea0106 [Moștenirea între două clase] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | <hidden>===== Laborator 06 - Moștenire simplă ===== | + | ===== Laborator 06 - Moștenire simplă ===== |
| **Autor: Răzvan Cristea** | **Autor: Răzvan Cristea** | ||
| Line 9: | Line 9: | ||
| * recunoască și să înțeleagă conceptul de moștenire între două clase | * recunoască și să înțeleagă conceptul de moștenire între două clase | ||
| * construiască legături între clase folosind relația de tip "is-a" (relație de specializare) | * construiască legături între clase folosind relația de tip "is-a" (relație de specializare) | ||
| - | * folosească membrii marcați cu protected și să înțeleagă diferențele dintre accesul public, privat și protejat în moștenire | + | * folosească membrii marcați cu protected și să înțeleagă diferențele dintre tipurile de acces |
| * aplice principiile de reutilizare a codului prin extinderea funcționalității clasei de bază în clasa derivată | * aplice principiile de reutilizare a codului prin extinderea funcționalității clasei de bază în clasa derivată | ||
| Line 21: | Line 21: | ||
| 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 și 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. | 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 și 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. | ||
| - | Î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)**. | + | Înainte de a explica moștenirea între două clase vom face o scurtă prezentare a modului în care trebuie să gestionăm mai multe fișiere header într-un proiect. Pentru acest laborator propunem clasa **Locuinta** care are ca și membri **pret (de tip float)** și **adresa (șir de caractere alocat dinamic)**. |
| <code cpp> | <code cpp> | ||
| Line 30: | Line 30: | ||
| class Locuinta | class Locuinta | ||
| { | { | ||
| - | float pret; | + | float pret; |
| - | char* adresa; | + | char* adresa; |
| public: | public: | ||
| - | Locuinta(); | + | Locuinta(); |
| - | Locuinta(const float& pret, const char* adresa); | + | Locuinta(const float& pret, const char* adresa); |
| - | Locuinta(const Locuinta& locuinta); | + | Locuinta(const Locuinta& locuinta); |
| - | Locuinta& operator=(const Locuinta& locuinta); | + | Locuinta& operator=(const Locuinta& locuinta); |
| - | ~Locuinta(); | + | ~Locuinta(); |
| - | float getPret() const; | + | float getPret() const; |
| - | char* getAdresa() const; | + | char* getAdresa() const; |
| - | void setPret(const float& pret); | + | void setPret(const float& pret); |
| - | void setAdresa(const char* adresa); | + | void setAdresa(const char* adresa); |
| - | friend std::ostream& operator<<(std::ostream& out, const Locuinta& locuinta); | + | friend std::ostream& operator<<(std::ostream& out, const Locuinta& locuinta); |
| }; | }; | ||
| </code> | </code> | ||
| Line 53: | Line 53: | ||
| <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> | <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> | ||
| - | Dacă voiam să folosim varianta tradițională de scriere a unui fișier header am fi procedat în maniera următoare. | + | Dacă am dori să folosim varianta tradițională de scriere a unui fișier header, astfel încât acesta să fie inclus o singură dată, am putea proceda în maniera următoare. |
| <code cpp> | <code cpp> | ||
| Line 107: | Line 107: | ||
| class Apartament : public Locuinta // clasa Apartament mosteneste clasa Locuinta | class Apartament : public Locuinta // clasa Apartament mosteneste clasa Locuinta | ||
| { | { | ||
| - | int numarCamere; | + | int numarCamere; |
| - | char* numeProprietar; | + | char* numeProprietar; |
| public: | public: | ||
| - | Apartament(); | + | Apartament(); |
| - | Apartament(const float& pret, const char* adresa, const int& numarCamere, const char* numeProprietar); | + | Apartament(const float& pret, const char* adresa, const int& numarCamere, const char* numeProprietar); |
| - | Apartament(const Apartament& apartament); | + | Apartament(const Apartament& apartament); |
| - | Apartament& operator=(const Apartament& apartament); | + | Apartament& operator=(const Apartament& apartament); |
| - | ~Apartament(); | + | ~Apartament(); |
| - | int getNumarCamere() const; | + | int getNumarCamere() const; |
| - | char* getNumeProprietar() const; | + | char* getNumeProprietar() const; |
| - | void setNumarCamere(const int& numarCamere); | + | void setNumarCamere(const int& numarCamere); |
| - | void setNumarCamere(const char* numeProprietar); | + | void setNumeProprietar(const char* numeProprietar); |
| - | friend std::ostream& operator<<(std::ostream& out, const Apartament& apartament); | + | friend std::ostream& operator<<(std::ostream& out, const Apartament& apartament); |
| }; | }; | ||
| </code> | </code> | ||
| Line 143: | Line 143: | ||
| protected: // specificatorul de acces protected | protected: // specificatorul de acces protected | ||
| - | float pret; | + | float pret; |
| - | char* adresa; | + | char* adresa; |
| public: | public: | ||
| - | Locuinta(); | + | Locuinta(); |
| - | Locuinta(const float& pret, const char* adresa); | + | Locuinta(const float& pret, const char* adresa); |
| - | Locuinta(const Locuinta& locuinta); | + | Locuinta(const Locuinta& locuinta); |
| - | Locuinta& operator=(const Locuinta& locuinta); | + | Locuinta& operator=(const Locuinta& locuinta); |
| - | ~Locuinta(); | + | ~Locuinta(); |
| - | float getPret() const; | + | float getPret() const; |
| - | char* getAdresa() const; | + | char* getAdresa() const; |
| - | void setPret(const float& pret); | + | void setPret(const float& pret); |
| - | void setAdresa(const char* adresa); | + | void setAdresa(const char* adresa); |
| - | friend std::ostream& operator<<(std::ostream& out, const Locuinta& locuinta); | + | friend std::ostream& operator<<(std::ostream& out, const Locuinta& locuinta); |
| }; | }; | ||
| </code> | </code> | ||
| Line 179: | Line 179: | ||
| Apartament::Apartament() : Locuinta() | Apartament::Apartament() : Locuinta() | ||
| { | { | ||
| - | numarCamere = 0; | + | numarCamere = 0; |
| - | numeProprietar = nullptr; | + | numeProprietar = nullptr; |
| } | } | ||
| </code> | </code> | ||
| Line 191: | Line 191: | ||
| Apartament::Apartament(const float& pret, const char* adresa, const int& numarCamere, const char* numeProprietar) : Locuinta(pret, adresa) | Apartament::Apartament(const float& pret, const char* adresa, const int& numarCamere, const char* numeProprietar) : Locuinta(pret, adresa) | ||
| { | { | ||
| - | this->numarCamere = numarCamere; | + | this->numarCamere = numarCamere; |
| - | if (numeProprietar != nullptr) | + | if (numeProprietar != nullptr) |
| - | { | + | { |
| - | this->numeProprietar = new char[strlen(numeProprietar) + 1]; | + | this->numeProprietar = new char[strlen(numeProprietar) + 1]; |
| - | strcpy(this->numeProprietar, numeProprietar); | + | strcpy(this->numeProprietar, numeProprietar); |
| - | } | + | } |
| - | else | + | else |
| - | { | + | { |
| - | this->numeProprietar = nullptr; | + | this->numeProprietar = nullptr; |
| - | } | + | } |
| } | } | ||
| </code> | </code> | ||
| Line 214: | Line 214: | ||
| Apartament::Apartament(const Apartament& apartament) : Locuinta(apartament) | Apartament::Apartament(const Apartament& apartament) : Locuinta(apartament) | ||
| { | { | ||
| - | numarCamere = apartament.numarCamere; | + | numarCamere = apartament.numarCamere; |
| - | if (apartament.numeProprietar != nullptr) | + | if (apartament.numeProprietar != nullptr) |
| - | { | + | { |
| - | numeProprietar = new char[strlen(apartament.numeProprietar) + 1]; | + | numeProprietar = new char[strlen(apartament.numeProprietar) + 1]; |
| - | strcpy(numeProprietar, apartament.numeProprietar); | + | strcpy(numeProprietar, apartament.numeProprietar); |
| - | } | + | } |
| - | else | + | else |
| - | { | + | { |
| - | numeProprietar = nullptr; | + | numeProprietar = nullptr; |
| - | } | + | } |
| } | } | ||
| </code> | </code> | ||
| Line 237: | Line 237: | ||
| Apartament::~Apartament() | Apartament::~Apartament() | ||
| { | { | ||
| - | if (numeProprietar != nullptr) | + | if (numeProprietar != nullptr) |
| - | { | + | { |
| - | delete[] numeProprietar; | + | delete[] numeProprietar; |
| - | } | + | } |
| } | } | ||
| </code> | </code> | ||
| Line 253: | Line 253: | ||
| Apartament& Apartament::operator=(const Apartament& apartament) | Apartament& Apartament::operator=(const Apartament& apartament) | ||
| { | { | ||
| - | if (this == &apartament) | + | if (this == &apartament) |
| - | { | + | { |
| - | return *this; | + | 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*/ | ||
| - | this->Locuinta::operator=(apartament); // se apeleaza operatorul de asignare din clasa parinte | + | if (numeProprietar != nullptr) |
| - | /*(Locuinta&)(*this) = apartament; // este echivalent cu linia de mai sus doar ca este o alta forma de apel*/ | + | { |
| + | delete[] numeProprietar; | ||
| + | } | ||
| - | numarCamere = apartament.numarCamere; | + | numarCamere = apartament.numarCamere; |
| - | if (apartament.numeProprietar != nullptr) | + | if (apartament.numeProprietar != nullptr) |
| - | { | + | { |
| - | numeProprietar = new char[strlen(apartament.numeProprietar) + 1]; | + | numeProprietar = new char[strlen(apartament.numeProprietar) + 1]; |
| - | strcpy(numeProprietar, apartament.numeProprietar); | + | strcpy(numeProprietar, apartament.numeProprietar); |
| - | } | + | } |
| - | else | + | else |
| - | { | + | { |
| - | numeProprietar = nullptr; | + | numeProprietar = nullptr; |
| - | } | + | } |
| - | return *this; | + | return *this; |
| } | } | ||
| </code> | </code> | ||
| Line 284: | Line 289: | ||
| std::ostream& operator<<(std::ostream& out, const Apartament& apartament) | std::ostream& operator<<(std::ostream& out, const Apartament& apartament) | ||
| { | { | ||
| - | operator<<(out, (Locuinta&)apartament); // chemam operatorul << din clasa parinte | + | operator<<(out, (Locuinta&)apartament); // chemam operatorul << din clasa parinte |
| + | /*out << (Locuinta&)apartament; // o alta forma de a chema operatorul << din clasa parinte*/ | ||
| - | out << "Numarul de camere din apartament este: " << apartament.numarCamere << " ron\n"; | + | out << "Numarul de camere din apartament este: " << apartament.numarCamere << " ron\n"; |
| - | if (apartament.numeProprietar != nullptr) | + | if (apartament.numeProprietar != nullptr) |
| - | { | + | { |
| - | out << "Numele proprietarului este: " << apartament.numeProprietar << "\n"; | + | out << "Numele proprietarului este: " << apartament.numeProprietar << "\n"; |
| - | } | + | } |
| - | else | + | else |
| - | { | + | { |
| - | out << "Numele proprietarului este: N/A\n"; | + | out << "Numele proprietarului este: N/A\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> | + | <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 privi lucrurile astfel: prietenii părinților noștri **nu sunt neapărat** și prietenii noștri. Relația de prietenie este specifică **doar** între părinții noștri și acele persoane, iar aceasta **nu se extinde automat** asupra noastră. La fel, funcțiile **friend** sunt prietene ale **clasei părinte**, dar **nu** devin prietene implicit și pentru **clasa derivată**.</note> |
| ==== ==== | ==== ==== | ||
| Line 319: | Line 325: | ||
| Î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**. | Î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.</hidden> | + | 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. |