Differences

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

Link to this comparison view

poo-is-ab:laboratoare:12 [2024/12/17 08:26]
razvan.cristea0106 [Rezolvarea Problemei Rombului]
poo-is-ab:laboratoare:12 [2025/01/19 22:31] (current)
razvan.cristea0106
Line 21: Line 21:
 {{ :​poo-is-ab:​laboratoare:​diamond.jpg?​direct&​400 |}} {{ :​poo-is-ab:​laboratoare:​diamond.jpg?​direct&​400 |}}
  
-Așa cum se poate observa și în imaginea de mai sus clasa **D** are două copii ale clasei **A**, ceea ce duce la **ambiguități** și **inconsistențe**. De exemplu, dacă încercăm să accesăm un membru al clasei **A** din clasa **D**, compilatorul **nu** poate determina ​pe unde să o ia, deoarece atât calea **D-B-A** cât și **D-C-A** duc spre clasa **A**.+Așa cum se poate observa și în imaginea de mai sus clasa **D** are două copii ale clasei **A**, ceea ce duce la **ambiguități** și **inconsistențe**. De exemplu, dacă încercăm să accesăm un membru al clasei **A** din clasa **D**, compilatorul **nu** poate determina ​care este drumul corect, deoarece atât calea **D-B-A** cât și calea **D-C-A** duc spre clasa **A**.
  
 În limbajele **C#** și **Java**, moștenirea multiplă a claselor **nu** este permisă tocmai pentru a evita astfel de probleme. În schimb, aceste limbaje oferă mecanisme alternative,​ precum **interfețele**,​ care permit implementarea a numeroase comportamente **fără** a introduce **conflicte** legate de **ambiguitatea moștenirii**. ​ În limbajele **C#** și **Java**, moștenirea multiplă a claselor **nu** este permisă tocmai pentru a evita astfel de probleme. În schimb, aceste limbaje oferă mecanisme alternative,​ precum **interfețele**,​ care permit implementarea a numeroase comportamente **fără** a introduce **conflicte** legate de **ambiguitatea moștenirii**. ​
Line 31: Line 31:
 ==== Rezolvarea Problemei Rombului ==== ==== Rezolvarea Problemei Rombului ====
  
-Pentru a putea gestiona și rezolva corect această problemă ​mai întâi ​trebuie să aplicăm doi pași si anume: **derivarea virtuala a claselor intermediare din clasa de baza** și respectiv **testarea aplicației** pentru ​a vedea dacă se comportă în conformitate cu așteptările pe care le avem.+Pentru a putea gestiona și rezolva corect această problemă trebuie să aplicăm doi pași si anume: **derivarea virtuala a claselor intermediare din clasa de baza** și respectiv **apelarea constructorilor clasei de bază în lista de inițializare a constructorilor clasei nepot**. Pentru ​a vedea dacă aplicația ​se comportă în conformitate cu așteptările pe care le avem va trebui să o testăm constant pentru a vedea dacă am eliminat toate ambiguitățile generate de problema rombului.
  
 === Derivarea virtuală === === Derivarea virtuală ===
Line 99: Line 99:
 </​code>​ </​code>​
  
-Dacă vom inițializa în funcția main un obiect de tipul clasei **D** vom observa că se va apela de două ori **constructorul** clasei **A** iar **destructorul** clasei **A** se va apela tot de două ori atunci când durata de viață a obiectului se va încheia.+Dacă vom inițializa în **funcția main** un obiect de tipul clasei **D** vom observa că se va apela de două ori **constructorul** clasei **A** iar **destructorul** clasei **A** se va apela tot de două ori atunci când durata de viață a obiectului se va încheia.
  
 <code cpp> <code cpp>
Line 114: Line 114:
 </​code>​ </​code>​
  
-<note warning>​Comportamentul descris mai sus apare din cauza **problemei rombului**, care generează o **ambiguitate** ce conduce la **dublul apel** al constructorului și destructorului clasei de bază **A**. Această situație poate deveni problematică în special în scenariile în care superclasa **A** gestionează resurse **alocate dinamic**. În astfel de cazuri, **ambiguitatea** poate duce la comportament nedefinit, cum ar fi **memory leaks** sau chiar **crash-uri** ale aplicației,​ deoarece **destructorul** poate fi apelat de mai multe ori pe **aceeași resursă**.</​note>​+Iar output-ul arată ca mai jos. 
 + 
 +<​file>​ 
 +Constructor A 
 +Constructor B 
 +Constructor A 
 +Constructor C 
 +Constructor D 
 + 
 +Destructor D 
 +Destructor C 
 +Destructor A 
 +Destructor B 
 +Destructor A 
 +</​file>​ 
 + 
 +<note warning>​Comportamentul descris mai sus apare din cauza **problemei rombului**, care generează o **ambiguitate** ce conduce la **dublul apel** al constructorului și al destructorului clasei de bază **A**. Această situație poate deveni problematică în special în scenariile în care superclasa **A** gestionează resurse **alocate dinamic**. În astfel de cazuri, **ambiguitatea** poate duce la **comportament nedefinit**, cum ar fi **memory leaks** sau chiar **crash-uri** ale aplicației,​ deoarece **destructorul** poate fi apelat de mai multe ori pe **aceeași resursă**.</​note>​
  
 Rezolvarea acestei probleme este **derivarea virtuală** a claselor intermediare **B** și respectiv **C** după cum urmează. Rezolvarea acestei probleme este **derivarea virtuală** a claselor intermediare **B** și respectiv **C** după cum urmează.
Line 182: Line 198:
 <note important>​În lista de inițializare a constructorului clasei **D** este obligatoriu să apelăm explicit constructorul clasei **A**, deoarece în cazul **moștenirii virtuale**, **constructorii** clasei de bază **nu** mai sunt **apelați automat** prin intermediul claselor intermediare. **Derivarea virtuală** modifică lanțul de apeluri ale constructorilor,​ transferând responsabilitatea inițializării clasei de bază **direct** către **clasa derivată finală**. Această cerință asigură că resursele sau membrii clasei **A** sunt inițializați **corect** și **elimină ambiguitatea** în procesul de construcție a obiectelor.</​note>​ <note important>​În lista de inițializare a constructorului clasei **D** este obligatoriu să apelăm explicit constructorul clasei **A**, deoarece în cazul **moștenirii virtuale**, **constructorii** clasei de bază **nu** mai sunt **apelați automat** prin intermediul claselor intermediare. **Derivarea virtuală** modifică lanțul de apeluri ale constructorilor,​ transferând responsabilitatea inițializării clasei de bază **direct** către **clasa derivată finală**. Această cerință asigură că resursele sau membrii clasei **A** sunt inițializați **corect** și **elimină ambiguitatea** în procesul de construcție a obiectelor.</​note>​
  
-Iar dacă vom testa în funcția ​**main** această ierarhie de clase vom vedea comportamentul așteptat.+Iar dacă vom testa în **funcția main** această ierarhie de clase vom obține rezultatul dorit.
  
 <code cpp> <code cpp>
 +#include <​iostream>​
 +
 int main() int main()
 { {
Line 214: Line 232:
  
 <code cpp> <code cpp>
 +#include <​iostream>​
 +
 int main() int main()
 { {
Line 238: Line 258:
 Deși ordinea apelării constructorilor este cea firească în cazul destructorilor putem observa că aceștia **nu** se apelează în **ordinea inversă** constructorilor,​ ceea ce înseamnă că la **dezalocarea memoriei** pentru pointerul **obj** nu se produce o **legătură întârziată (late binding)**. Deși ordinea apelării constructorilor este cea firească în cazul destructorilor putem observa că aceștia **nu** se apelează în **ordinea inversă** constructorilor,​ ceea ce înseamnă că la **dezalocarea memoriei** pentru pointerul **obj** nu se produce o **legătură întârziată (late binding)**.
  
-<note warning>​În cazul **problemei rombului** clasa **A** nu este o **clasă abstractă** sau o **interfață**. Toate clasele existente sunt **clase reale**, adică sunt **instanțiabile**,​ deci asigurarea legăturii întârziate se poate face **doar** prin intermediul **metodelor virtuale**.</​note>​+<note warning>​În cazul **problemei rombului** ​prezentate în acest laborator ​clasa **A** nu este o **clasă abstractă** sau o **interfață**. Toate clasele existente sunt **clase reale**, adică sunt **instanțiabile**,​ deci asigurarea ​**legăturii întârziate** se poate face **doar** prin intermediul **metodelor virtuale**.</​note>​
  
-Soluția este să marcăm destructorul clasei **A** ca fiind **virtual**,​ astfel **tabela virtuală de pointeri** va fi moștenită de clasele **B**, **C****D**.+Soluția este să marcăm destructorul clasei **A** ca fiind **virtual**,​ astfel **tabela virtuală de pointeri** va fi moștenită de clasele **B**, **C** și respectiv ​**D**.
  
 <code cpp> <code cpp>
Line 272: Line 292:
 Destructor A Destructor A
 </​file>​ </​file>​
 +
 +<note tip>​Trebuie menționat însă faptul că limbajul C++ **permite** organizarea în formă de **romb** și pentru **clasele abstracte** și respectiv **interfețe**. Spre exemplu putem avea patru **clase abstracte** organizate în formă de **romb** iar **clasa de la baza rombului (clasa nepot)** este moștenită de o **clasă instanțiabilă**.</​note>​
  
 ==== ==== ==== ====
  
-Este important să înțelegem că **problema rombului** nu reprezintă neapărat un concept negativ, ci mai degrabă o provocare specifică **moștenirii multiple**. Când apare, este esențial să o gestionăm cu atenție, pentru a evita **ambiguitățile** sau **comportamentele nedefinite** în timpul execuției programului. O gestionare corectă a acestei situații asigură un **cod robust**, **clar** și lipsit de **erori critice**, cum ar fi **memory leaks** sau conflicte în ordinea apelurilor **constructorilor** și respectiv **destructorilor**.+Este important să înțelegem că **problema rombului** nu reprezintă neapărat un concept negativ, ci mai degrabă o provocare specifică **moștenirii multiple**. Când apare, este esențial să o gestionăm cu atenție, pentru a evita **ambiguitățile** sau **comportamentele nedefinite** în timpul execuției programului. O gestionare corectă a acestei situații asigură un **cod robust**, **clar** și lipsit de **erori critice**, cum ar fi **memory leaks** sau **conflicte** în ordinea apelurilor **constructorilor** și respectiv **destructorilor**.
  
 ==== Concluzii ==== ==== Concluzii ====
Line 281: Line 303:
 **Problema rombului** evidențiază complexitatea **moștenirii multiple** și potențialele **capcane** care pot apărea în **proiectarea claselor** într-un limbaj precum **C++**. Deși **moștenirea multiplă** oferă **flexibilitate** și permite **reutilizarea codului**, aceasta vine cu riscuri, cum ar fi **ambiguitatea generată de apelurile multiple ale constructorilor** sau **destructorului clasei de bază**. Soluția **derivării virtuale** este un mecanism eficient pentru a **preveni** aceste ambiguități,​ oferind o **modalitate clară** de a gestiona relațiile dintre clase. Totuși, utilizarea **derivării virtuale** necesită o înțelegere profundă a modului în care funcționează apelurile constructorilor și cum să definim explicit ordinea acestora. ​ **Problema rombului** evidențiază complexitatea **moștenirii multiple** și potențialele **capcane** care pot apărea în **proiectarea claselor** într-un limbaj precum **C++**. Deși **moștenirea multiplă** oferă **flexibilitate** și permite **reutilizarea codului**, aceasta vine cu riscuri, cum ar fi **ambiguitatea generată de apelurile multiple ale constructorilor** sau **destructorului clasei de bază**. Soluția **derivării virtuale** este un mecanism eficient pentru a **preveni** aceste ambiguități,​ oferind o **modalitate clară** de a gestiona relațiile dintre clase. Totuși, utilizarea **derivării virtuale** necesită o înțelegere profundă a modului în care funcționează apelurile constructorilor și cum să definim explicit ordinea acestora. ​
  
-**Diamond problem** nu este ceva rău în sine, ci o **oportunitate** de a învăța să scriem cod **bine structurat** și de a ne baza pe mecanismele oferite de limbajul de programare pentru a rezolva **ambiguitățile**. Printr-o planificare **atentă** a ierarhiilor de clase și aplicarea **corectă** a **derivării virtuale**, putem evita **erori critice**, precum **memory leaks** sau **comportamente imprevizibile**. De asemenea, această problemă subliniază importanța **testării riguroase** și a **înțelegerii detaliate** a **relațiilor dintre clase** în programele complexe.+**Diamond problem** nu este ceva rău în sine, ci o **oportunitate** de a învăța să scriem ​un cod **bine structurat** și de a ne baza pe mecanismele oferite de limbajul de programare ​utilizat ​pentru a rezolva **ambiguitățile**. Printr-o planificare **atentă** a ierarhiilor de clase și aplicarea **corectă** a **derivării virtuale**, putem evita **erori critice**, precum **memory leaks** sau **comportamente imprevizibile**. De asemenea, această problemă subliniază importanța **testării riguroase** și a **înțelegerii detaliate** a **relațiilor dintre clase** în programele complexe.
poo-is-ab/laboratoare/12.1734416790.txt.gz · Last modified: 2024/12/17 08: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