This shows you the differences between two versions of the page.
|
poo-is-ab:laboratoare:09 [2025/11/23 14:36] razvan.cristea0106 [Interfețe] |
poo-is-ab:laboratoare:09 [2025/12/09 11:26] (current) razvan.cristea0106 [Metode virtuale] |
||
|---|---|---|---|
| Line 19: | Line 19: | ||
| De exemplu, dacă avem o funcție **''adunare''** supraîncărcată pentru a lucra cu numere întregi și cu numere reale, **compilatorul** determină **automat** care versiune a funcției urmează să fie apelată, în funcție de **tipul** datelor primite ca argumente. Avantajul acestui tip de polimorfism este **viteza de execuție**, deoarece decizia a fost deja luată **înainte** ca programul să ruleze. | De exemplu, dacă avem o funcție **''adunare''** supraîncărcată pentru a lucra cu numere întregi și cu numere reale, **compilatorul** determină **automat** care versiune a funcției urmează să fie apelată, în funcție de **tipul** datelor primite ca argumente. Avantajul acestui tip de polimorfism este **viteza de execuție**, deoarece decizia a fost deja luată **înainte** ca programul să ruleze. | ||
| - | Cu toate acestea, există cazuri în care decizia pentru funcția/metoda ce trebuie apelată **nu** poate fi luată de către compilator, ci doar în timpul execuției. Acest procedeu este cunoscut sub denumirea de **polimorfism întârziat (late binding sau run-time polymorphism)**. Acest concept este strâns legat de **suprascrierea funcțiilor (overriding)** și de utilizarea mecanismelor de **moștenire** și **metode virtuale** în C++. | + | Cu toate acestea, există cazuri în care decizia pentru metoda ce trebuie apelată **nu** poate fi luată de către compilator, ci doar în timpul execuției. Acest procedeu este cunoscut sub denumirea de **polimorfism întârziat (late binding sau run-time polymorphism)**. Acest concept este strâns legat de **suprascrierea funcțiilor (overriding)** și de utilizarea mecanismelor de **moștenire** și **metode virtuale** în C++. |
| ==== Overloading vs Overriding ==== | ==== Overloading vs Overriding ==== | ||
| Line 28: | Line 28: | ||
| | Se aplică funcțiilor/metodelor din **aceeași clasă, același namespace, sau același fișier** | Apare în ierarhiile de clase (**moștenire**) | | | Se aplică funcțiilor/metodelor din **aceeași clasă, același namespace, sau același fișier** | Apare în ierarhiile de clase (**moștenire**) | | ||
| | Funcțiile/metodele au **același nume**, dar **diferă** prin **numărul, tipul sau ordinea parametrilor** | O metodă dintr-o clasă derivată **suprascrie** comportamentul unei **metode virtuale** din clasa de bază | | | Funcțiile/metodele au **același nume**, dar **diferă** prin **numărul, tipul sau ordinea parametrilor** | O metodă dintr-o clasă derivată **suprascrie** comportamentul unei **metode virtuale** din clasa de bază | | ||
| - | | Alegerea funcției/metodei este făcută de către **compilator** pe baza semnăturii acesteia | Alegerea metodei care va fi apelată este făcută la **momentul execuției**, în funcție de **tipul dinamic** al obiectului | | + | | Alegerea funcției/metodei este făcută de către **compilator** pe baza semnăturii acesteia | Alegerea metodei care va fi apelată este făcută la **runtime**, în funcție de **tipul dinamic** al obiectului | |
| - | | **Nu** necesită metode virtuale/virtual pure. | **Necesită** utilizarea metodelor virtuale/virtual pure în clasa de bază. | | + | | **Nu** necesită metode virtuale/virtual pure | **Necesită** utilizarea metodelor virtuale/virtual pure în clasa de bază | |
| | Este o formă de polimorfism timpuriu (**early binding**) | Este o formă de polimorfism întârziat (**late binding**) | | | Este o formă de polimorfism timpuriu (**early binding**) | Este o formă de polimorfism întârziat (**late binding**) | | ||
| | Are un impact **redus** asupra performanței, deoarece decizia se ia la compilare | Are un impact **ușor mai mare** asupra performanței, deoarece decizia se ia în timpul execuției | | | Are un impact **redus** asupra performanței, deoarece decizia se ia la compilare | Are un impact **ușor mai mare** asupra performanței, deoarece decizia se ia în timpul execuției | | ||
| Line 51: | Line 51: | ||
| **Object slicing** reprezintă situația în care, la copierea unui obiect **derivat** într-o variabilă de **tipul clasei de bază**, se păstrează doar **partea de bază** a obiectului, iar atributele și comportamentele specifice **clasei derivate** sunt **„tăiate” (eliminate)**, obiectul comportându-se ca unul de **tipul clasei de bază**.</note> | **Object slicing** reprezintă situația în care, la copierea unui obiect **derivat** într-o variabilă de **tipul clasei de bază**, se păstrează doar **partea de bază** a obiectului, iar atributele și comportamentele specifice **clasei derivate** sunt **„tăiate” (eliminate)**, obiectul comportându-se ca unul de **tipul clasei de bază**.</note> | ||
| - | <note>În C++ există **două** tipuri de metode virtuale și anume: **metode virtuale** și **metode virtual pure**. Diferența între cele două tipuri constă în faptul că o metodă virtual pură **nu** are implementare în **clasa de bază**.</note> | + | Pentru a putea înțelege mai bine cum se poate realiza **legătura întârziată (late binding)** și cum apare fenomenul de **Object Slicing** vom propune un exemplu simplu de cod. Pentru a putea declara o metodă virtuală în C++ se folosește cuvântul cheie **virtual** care anunță compilatorul că metoda poate fi **suprascrisă** în clasele derivate și că aceasta trebuie să fie gestionată prin intermediul **vtable**. |
| - | În limbajul C++ pentru a declara o funcție virtuală într-o clasă se utilizeaza cuvântul cheie **virtual**. Dacă am spus funcție virtuală asta înseamnă că în clasa de bază acea funcție va avea implementare. Ca și exemplu vom propune clasele **Animal** și **Caine**. | + | <code cpp> |
| + | #include <iostream> | ||
| + | |||
| + | class Baza | ||
| + | { | ||
| + | public: | ||
| + | |||
| + | virtual void afisare() const // metoda virtuala | ||
| + | { | ||
| + | std::cout << "Sunt un obiect de tipul clasei Baza\n"; | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | class Derivata : public Baza | ||
| + | { | ||
| + | public: | ||
| + | |||
| + | void afisare() const // aceasta functie membra suprascrie comportamentul metodei afisare din clasa parinte | ||
| + | { | ||
| + | std::cout << "Sunt un obiect de tipul clasei Derivata\n"; | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | Derivata obj; | ||
| + | |||
| + | // 1. FARA slicing — prin pointer | ||
| + | |||
| + | Baza* ptr = &obj; | ||
| + | ptr->afisare(); // se apeleaza metoda din clasa Derivata | ||
| + | |||
| + | // 2. FARA slicing — prin referinta | ||
| + | |||
| + | Baza& ref = obj; | ||
| + | ref.afisare(); // se apeleaza metoda din clasa Derivata | ||
| + | |||
| + | // 3. CU slicing — prin copiere (valoare) | ||
| + | |||
| + | Baza b = obj; // aici se produce fenomenul de Object Slicing | ||
| + | b.afisare(); // se apeleaza metoda din Baza (partea din clasa Derivata este "taiata") | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | <note>În C++ există **două** tipuri de metode virtuale și anume: **metode virtuale** și **metode virtual pure**. Diferența între cele două tipuri constă în faptul că unei metode virtual pure **îi poate lipsi** implementarea în **clasa de bază**.</note> | ||
| + | |||
| + | În continuare propunem ca și exemplu practic clasele **Animal** și **Caine**, unde ne dorim să punem în evidență conceputul de **late binding** și să adăugăm informații noi cu privire la acestă noțiune nouă pe care o învățăm. | ||
| <code cpp> | <code cpp> | ||
| Line 85: | Line 133: | ||
| </code> | </code> | ||
| - | În funcția **main** vom demostra faptul că deși câinele este un animal se vor apela metodele specifice tipurilor de date din cauza faptului că în clasa Animal metoda **''afisare''** nu este declarată ca fiind virtuală. | + | În funcția **main** vom demostra faptul că deși câinele este un animal, totuși vom observa că se vor apela metodele specifice tipurilor de date din cauza faptului că în clasa Animal metoda **''afisare''** nu este declarată ca fiind virtuală. |
| <code cpp> | <code cpp> | ||
| Line 167: | Line 215: | ||
| </code> | </code> | ||
| - | <note important>Cuvântul cheie **override** în **C++** este folosit pentru a specifica în mod **explicit** că o funcție membră dintr-o **clasă derivată** suprascrie o metodă **virtuală** din **clasa de bază**. Acest mecanism oferă mai multă siguranță în ceea ce privește **suprascrierea** metodelor și ajută la prevenirea **erorilor de programare**.</note> | + | <note important>Cuvântul cheie **override** în **C++** este folosit pentru a specifica în mod **explicit** că o funcție membră dintr-o **clasă derivată** suprascrie o **metodă virtuală** din **clasa de bază**. Acest mecanism oferă mai multă siguranță în ceea ce privește **suprascrierea** metodelor și ajută la prevenirea **erorilor de programare**.</note> |
| ==== Clase abstracte ==== | ==== Clase abstracte ==== | ||
| - | În limbajul C++, o **clasă abstractă** este o clasă care conține **cel puțin** o **metodă virtuală pură**. **Metoda virtuală pură** este o funcție declarată în clasa de bază, dar care **nu are o implementare** în această clasă, **obligând** astfel **clasele derivate** să o **suprascrie**. O **clasă abstractă** este utilizată pentru a defini un comportament **general** care trebuie să fie specificat în **mod detaliat** în **clasele derivate**. | + | În limbajul C++, o **clasă abstractă** este o clasă care conține **cel puțin** o **metodă virtuală pură**. **Metoda virtuală pură** este o funcție membră declarată în clasa de bază, dar care **nu are neapărat o implementare** în această clasă, **obligând** astfel **clasele derivate** să o **suprascrie**. O **clasă abstractă** este utilizată pentru a defini un comportament **general** care trebuie să fie specificat în **mod detaliat** în **clasele derivate**. |
| - | <note warning>O clasă abstractă **nu** poate fi folosită pentru a crea **obiecte**. Este concepută să fie doar o bază pentru alte clase care o vor moșteni. În schimb se pot instanția **pointeri** de tipul acestei clase care să fie inițializati cu ajutorul **claselor derivate**. Un alt aspect ce trebuie menționat este faptul că **orice** clasă derivată dintr-o clasă abstractă **trebuie** să implementeze **toate** metodele virtual pure, altfel va deveni ea însăși o **clasă abstractă**.</note> | + | <note warning>O clasă abstractă **nu** poate fi folosită pentru a crea **obiecte**. Este concepută să fie doar o bază pentru alte clase care o vor **moșteni**. În schimb se pot instanția **pointeri** de tipul acestei clase care să fie inițializati cu ajutorul **claselor derivate**. Un alt aspect ce trebuie menționat este faptul că **orice** clasă derivată dintr-o clasă abstractă **trebuie** să implementeze **toate** metodele virtual pure, altfel va deveni ea însăși o **clasă abstractă**.</note> |
| O clasă abstractă poate avea membri și metode precum constructori, getteri, setteri și destructor care vor fi apelate în clasele derivate. În continuare vom prezenta modul în care putem pune în evidență conceptul de **late binding** transformând clasa **Animal** într-o clasă abstractă. | O clasă abstractă poate avea membri și metode precum constructori, getteri, setteri și destructor care vor fi apelate în clasele derivate. În continuare vom prezenta modul în care putem pune în evidență conceptul de **late binding** transformând clasa **Animal** într-o clasă abstractă. | ||
| Line 320: | Line 368: | ||
| ^ Caracteristică ^ Interfață ^ Clasă abstractă ^ | ^ Caracteristică ^ Interfață ^ Clasă abstractă ^ | ||
| - | | **Metode** | Conține doar metode virtual pure | Are cel puțin o metodă virtuală pură și poate include metode virtuale și metode concrete, în orice număr | | + | | **Metode** | Conține doar metode virtual pure | Are cel puțin o metodă virtuală pură și poate include metode virtuale și metode concrete | |
| | **Membri de date** | Nu poate avea membri | Poate avea membri | | | **Membri de date** | Nu poate avea membri | Poate avea membri | | ||
| | **Constructori** | Nu poate avea constructori | Poate avea constructori | | | **Constructori** | Nu poate avea constructori | Poate avea constructori | | ||
| - | | **Moștenire multiplă** | Este utilizată pentru moștenirea multiplă, mai ales pentru a defini contracte comune | Este utilizată în ierarhii simple sau complexe, dar poate genera ambiguități în moștenirea multiplă | | + | | **Moștenire multiplă** | Este utilizată pentru moștenirea multiplă, mai ales pentru a defini contracte comune | Este utilizată în ierarhii de clase, dar poate genera ambiguități în moștenirea multiplă | |
| | **Scop** | Definește un contract strict pentru clasele derivate | Definește un comportament parțial și oferă reutilizarea codului | | | **Scop** | Definește un contract strict pentru clasele derivate | Definește un comportament parțial și oferă reutilizarea codului | | ||
| - | | **Destructor** | Necesită destructor virtual pur atunci când în clasele derivate există membri de tip pointer | Destructorul virtual care trebuie implementat când există pointeri în clasă | | + | | **Destructor** | Destructorul virtual pur este obligatoriu atunci când în clasele derivate există membri de tip pointer alocați dinamic | Destructorul virtual trebuie implementat atunci când există pointeri alocați dinamic în clasele derivate | |
| | **Instanțiere** | Nu poate fi instanțiată, dar se pot utiliza pointeri la tipul acesteia | Nu poate fi instanțiată, dar poate avea constructori pentru clase derivate | | | **Instanțiere** | Nu poate fi instanțiată, dar se pot utiliza pointeri la tipul acesteia | Nu poate fi instanțiată, dar poate avea constructori pentru clase derivate | | ||
| Line 448: | Line 496: | ||
| * **Overriding** este asociat cu polimorfismul dinamic (**run-time polymorphism**) și presupune redefinirea comportamentului unei metode virtuale din clasa de bază în clasele derivate. | * **Overriding** este asociat cu polimorfismul dinamic (**run-time polymorphism**) și presupune redefinirea comportamentului unei metode virtuale din clasa de bază în clasele derivate. | ||
| - | * **Overloading** este asociat cu polimorfismul timpuriu (**compile-time polymorphism**) și permite mai multe metode cu același nume, dar semnături diferite, în cadrul aceleiași clase. | + | * **Overloading** este asociat cu polimorfismul timpuriu (**compile-time polymorphism**) și permite mai multe metode cu același nume, dar cu semnături diferite. |
| * Este important să folosim cuvântul cheie **override** pentru a face codul mai sigur și mai lizibil, prevenind erorile legate de semnături greșite sau lipsa suprascrierii. | * Este important să folosim cuvântul cheie **override** pentru a face codul mai sigur și mai lizibil, prevenind erorile legate de semnături greșite sau lipsa suprascrierii. | ||