This shows you the differences between two versions of the page.
|
poo-is-ab:laboratoare:10 [2025/12/01 13:40] razvan.cristea0106 [Supraîncărcarea operatorului << pentru o clasă abstractă] |
poo-is-ab:laboratoare:10 [2025/12/04 09:00] (current) razvan.cristea0106 [Vector de obiecte neomogene] |
||
|---|---|---|---|
| Line 46: | Line 46: | ||
| }; | }; | ||
| </code> | </code> | ||
| + | |||
| + | <note>Așa cum am discutat în cadrul laboratorului anterior, atunci când declarăm o metodă ca fiind **virtual pură**, clasa care o conține devine automat o **clasă abstractă**. Totuși, faptul că o **funcție membră** este marcată drept virtual pură **nu ne împiedică deloc** să îi oferim o implementare în clasa unde a fost declarată. Această marcare, exprimată prin sintaxa **''= 0''**, are rolul de a face clasa **neinstanțiabilă**, **nu de a interzice existența unei definiții**. Cu alte cuvinte, putem avea în continuare un **comportament de bază** pentru metoda respectivă, chiar dacă **obligăm subclasele** să o **suprascrie**.</note> | ||
| + | |||
| + | În cele ce urmează vom exemplifica printr-o secvență de cod ceea ce am menționat anterior. | ||
| + | |||
| + | <code cpp> | ||
| + | #include <iostream> | ||
| + | |||
| + | class A | ||
| + | { | ||
| + | public: | ||
| + | |||
| + | // Metoda este marcata ca fiind virtual pura -> clasa devine abstracta | ||
| + | // Totusi ii putem oferi o implementare | ||
| + | virtual void f() = 0; | ||
| + | }; | ||
| + | |||
| + | // Implementarea metodei virtual pure trebuie sa fie in afara clasei, | ||
| + | // deoarece limbajul C++ nu permite definirea unei metode virtual pure direct in interiorul clasei | ||
| + | void A::f() | ||
| + | { | ||
| + | std::cout << "Comportament default din A::f()\n"; | ||
| + | } | ||
| + | |||
| + | class B : public A | ||
| + | { | ||
| + | public: | ||
| + | |||
| + | // B suprascrie metoda f si foloseste implementarea din clasa de baza | ||
| + | void f() override | ||
| + | { | ||
| + | std::cout << "B::f() - inainte de apelul bazei\n"; | ||
| + | A::f(); // se apeleaza metoda superclasei | ||
| + | std::cout << "B::f() - dupa apelul bazei\n"; | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | class C : public A | ||
| + | { | ||
| + | public: | ||
| + | |||
| + | // C suprascrie metoda f si inlocuieste complet comportamentul | ||
| + | void f() override | ||
| + | { | ||
| + | std::cout << "C::f() - comportament complet redefinit\n"; | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | // A a; // eroare de compilare, clasa nu este instantiabila | ||
| + | // A* ptr = new A(); // nu se poate, clasa nu este instantiabila | ||
| + | |||
| + | A* p1 = new B(); | ||
| + | A* p2 = new C(); | ||
| + | |||
| + | std::cout << "--- Apel prin B ---\n"; | ||
| + | p1->f(); | ||
| + | |||
| + | std::cout << "\n--- Apel prin C ---\n"; | ||
| + | p2->f(); | ||
| + | |||
| + | delete p1; | ||
| + | delete p2; | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | În continuare propunem un tabel în care se prezintă caracteristicile metodelor virtuale și respectiv virtual pure pentru a rezuma explicațiile oferite mai sus. | ||
| + | |||
| + | ^ Caracteristică ^ Metodă virtuală ^ Metodă virtuală pură ^ | ||
| + | | Obligă clasa derivată să o suprascrie? | Nu | Da | | ||
| + | | Clasa devine abstractă? | Nu | Da | | ||
| + | | Are implementare în clasa de bază? | Da (obligatoriu) | Nu neapărat | | ||
| + | | Poate fi apelată din subclasă? | Da | Da | | ||
| + | | Sintaxă | ''virtual void f();'' | ''virtual void f() = 0;'' | | ||
| + | | Permite instanțierea clasei de bază? | Da | Nu | | ||
| + | | Apelabilă prin pointer la tipul bazei? | Da | Da | | ||
| <note important>În C++, unui **destructor virtual pur** trebuie să îi oferim o **implementare** deoarece va fi **întotdeauna apelat** atunci când un **obiect derivat** este **distrus**. Această cerință se bazează pe mecanismul de distrugere a obiectelor, care implică apelarea destructorilor **în ordine inversă** a **constructorilor**, inclusiv pentru clasa de bază.</note> | <note important>În C++, unui **destructor virtual pur** trebuie să îi oferim o **implementare** deoarece va fi **întotdeauna apelat** atunci când un **obiect derivat** este **distrus**. Această cerință se bazează pe mecanismul de distrugere a obiectelor, care implică apelarea destructorilor **în ordine inversă** a **constructorilor**, inclusiv pentru clasa de bază.</note> | ||
| - | Prin urmare vom furniza o implementare pentru destructorul clasei **ProdusElectronic** după cum urmează în secțiunea de cod de mai jos. | + | Odată înțelese conceptele privind metodele virtuale și virtual pure, putem oferi implementarea destructorului clasei **ProdusElectronic**, așa cum este ilustrată în codul următor. |
| <code cpp> | <code cpp> | ||
| Line 94: | Line 173: | ||
| </code> | </code> | ||
| - | Iar implementările metodelor celor două clase se pot observa în blocurile de mai jos. | + | Iar implementările metodelor celor două clase se pot observa în blocurile de cod de mai jos. |
| <code cpp> | <code cpp> | ||
| Line 247: | Line 326: | ||
| În contextul exemplului prezentat anterior, ar fi foarte elegant să putem afișa elementele vectorului folosind **operatorul %%<<%%**, ceea ce ar simplifica și uniformiza procesul de afișare. Totuși, o problemă fundamentală apare din faptul că acest operator poate fi **supraîncărcat**, dar nu și **suprascris**. | În contextul exemplului prezentat anterior, ar fi foarte elegant să putem afișa elementele vectorului folosind **operatorul %%<<%%**, ceea ce ar simplifica și uniformiza procesul de afișare. Totuși, o problemă fundamentală apare din faptul că acest operator poate fi **supraîncărcat**, dar nu și **suprascris**. | ||
| - | Prin urmare **operatorul %%<<%%** nu poate fi declarat **virtual**, astfel încât să permită apelul unei implementări specifice **clasei derivate** atunci când este utilizat printr-un **pointer** sau o **referință** la **clasa de bază**. Soluția implică de obicei definirea unei metode virtuale în **clasa abstractă** și utilizarea acesteia în supraîncărcarea **operatorului %%<<%%**. | + | Prin urmare **operatorul %%<<%%** nu poate fi declarat **virtual**, astfel încât să permită apelul unei implementări specifice **clasei derivate** atunci când este utilizat printr-un **pointer** sau o **referință** la **clasa de bază**. Soluția implică de obicei definirea unei metode virtuale în **clasa abstractă** și utilizarea acesteia în supraîncărcarea **operatorului de afișare %%<<%%**. |
| Această abordare oferă o separare clară între logica specifică de afișare și mecanismul **operatorului %%<<%%**, respectând în același timp principiile polimorfismului. | Această abordare oferă o separare clară între logica specifică de afișare și mecanismul **operatorului %%<<%%**, respectând în același timp principiile polimorfismului. | ||