This shows you the differences between two versions of the page.
|
poo-is-ab:laboratoare:12 [2025/01/11 12:08] razvan.cristea0106 [Rezolvarea Problemei Rombului] |
poo-is-ab:laboratoare:12 [2025/12/12 20:40] (current) razvan.cristea0106 [Rezolvarea Problemei Rombului] |
||
|---|---|---|---|
| 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 care este drumul corect, 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ă 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 virtuală a claselor intermediare din clasa de bază** ș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 42: | Line 42: | ||
| public: | public: | ||
| - | A() | + | A() |
| { | { | ||
| std::cout << "Constructor A\n"; | std::cout << "Constructor A\n"; | ||
| } | } | ||
| - | ~A() | + | ~A() |
| { | { | ||
| std::cout << "Destructor A\n"; | std::cout << "Destructor A\n"; | ||
| Line 53: | Line 53: | ||
| }; | }; | ||
| - | class B : public A | + | class B : public A |
| { | { | ||
| public: | public: | ||
| Line 62: | Line 62: | ||
| } | } | ||
| - | ~B() | + | ~B() |
| { | { | ||
| std::cout << "Destructor B\n"; | std::cout << "Destructor B\n"; | ||
| Line 68: | Line 68: | ||
| }; | }; | ||
| - | class C : public A | + | class C : public A |
| { | { | ||
| public: | public: | ||
| Line 77: | Line 77: | ||
| } | } | ||
| - | ~C() | + | ~C() |
| { | { | ||
| std::cout << "Destructor C\n"; | std::cout << "Destructor C\n"; | ||
| Line 83: | Line 83: | ||
| }; | }; | ||
| - | class D : public B, public C | + | class D : public B, public C |
| { | { | ||
| public: | public: | ||
| Line 92: | Line 92: | ||
| } | } | ||
| - | ~D() | + | ~D() |
| { | { | ||
| std::cout << "Destructor D\n"; | std::cout << "Destructor D\n"; | ||
| Line 104: | Line 104: | ||
| #include <iostream> | #include <iostream> | ||
| - | int main() | + | int main() |
| { | { | ||
| D obj; | D obj; | ||
| Line 113: | Line 113: | ||
| } | } | ||
| </code> | </code> | ||
| + | |||
| + | 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> | <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 situații de ambiguitate se face prin **derivarea virtuală** a claselor intermediare **B** și respectiv **C** după cum urmează în blocul de cod de mai jos. |
| <code cpp> | <code cpp> | ||
| Line 123: | Line 139: | ||
| public: | public: | ||
| - | A() | + | A() |
| - | { | + | { |
| - | std::cout << "Constructor A\n"; | + | std::cout << "Constructor A\n"; |
| - | } | + | } |
| - | ~A() | + | ~A() |
| - | { | + | { |
| - | std::cout << "Destructor A\n"; | + | std::cout << "Destructor A\n"; |
| - | } | + | } |
| }; | }; | ||
| Line 138: | Line 154: | ||
| public: | public: | ||
| - | B() : A() | + | B() : A() |
| - | { | + | { |
| - | std::cout << "Constructor B\n"; | + | std::cout << "Constructor B\n"; |
| - | } | + | } |
| - | ~B() | + | ~B() |
| - | { | + | { |
| - | std::cout << "Destructor B\n"; | + | std::cout << "Destructor B\n"; |
| - | } | + | } |
| }; | }; | ||
| Line 153: | Line 169: | ||
| public: | public: | ||
| - | C() : A() | + | C() : A() |
| - | { | + | { |
| - | std::cout << "Constructor C\n"; | + | std::cout << "Constructor C\n"; |
| - | } | + | } |
| - | ~C() | + | ~C() |
| - | { | + | { |
| - | std::cout << "Destructor C\n"; | + | std::cout << "Destructor C\n"; |
| - | } | + | } |
| }; | }; | ||
| Line 168: | Line 184: | ||
| public: | public: | ||
| - | D() : A(), B(), C() | + | D() : A(), B(), C() |
| - | { | + | { |
| - | std::cout << "Constructor D\n"; | + | std::cout << "Constructor D\n"; |
| - | } | + | } |
| - | ~D() | + | ~D() |
| - | { | + | { |
| - | std::cout << "Destructor D\n"; | + | std::cout << "Destructor D\n"; |
| - | } | + | } |
| }; | }; | ||
| </code> | </code> | ||
| Line 189: | Line 205: | ||
| int main() | int main() | ||
| { | { | ||
| - | D obj; | + | D obj; |
| - | std::cout << '\n'; | + | std::cout << '\n'; |
| - | return 0; | + | return 0; |
| } | } | ||
| </code> | </code> | ||
| Line 220: | Line 236: | ||
| int main() | int main() | ||
| { | { | ||
| - | A* obj = new D(); | + | A* obj = new D(); |
| - | std::cout << '\n'; | + | std::cout << '\n'; |
| - | delete obj; | + | delete obj; |
| - | return 0; | + | return 0; |
| } | } | ||
| </code> | </code> | ||
| Line 251: | Line 267: | ||
| public: | public: | ||
| - | A() | + | A() |
| - | { | + | { |
| - | std::cout << "Constructor A\n"; | + | std::cout << "Constructor A\n"; |
| - | } | + | } |
| - | virtual ~A() | + | virtual ~A() |
| - | { | + | { |
| - | std::cout << "Destructor A\n"; | + | std::cout << "Destructor A\n"; |
| - | } | + | } |
| }; | }; | ||
| </code> | </code> | ||