This shows you the differences between two versions of the page.
poo-is:laboratoare:05 [2020/09/20 15:42] ruben_gilian.udroiu [Exemplu amanuntit] |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ======= Laboratorul 05: Polimorfism. Agregare ======= | ||
- | In cadrul laboratorului 5 al Programarii Orientate pe Obiecte, vom aprofunda unul dintre cele 4 principii ale POO-ului, **polimorfismul**, si un alt concept important, **agregarea**. | ||
- | Ca referinte externe, recomandam urmatorul capitol din [[https://discourse-production.oss-cn-shanghai.aliyuncs.com/original/3X/2/3/2380479dcb8e375425884a10da232730bbc7f88d.pdf|Absolute C++]]: | ||
- | * Capitolul 15 (Chapter 15: Polymorphism and virtual functions, pag. 661-693) | ||
- | |||
- | ===== Polimorfism ===== | ||
- | <note important>**Polimorfism** = abilitatea unei functii cu un anumit nume sa aiba comportamente diferite, in functie de parametrii de intrare.</note> | ||
- | <note> Semnatura unei metode consta in: | ||
- | * numele metodei | ||
- | * numarul si tipul parametrilor | ||
- | </note> | ||
- | <note important>In C++, avem mai multe tipuri de polimorfism: | ||
- | * Compile Time Polymorphism | ||
- | * Run Time Polymorphism | ||
- | </note> | ||
- | <note>**Compile Time Polymorphism** | ||
- | |||
- | Acest tip de polimorfism este obtinut cand supraincarcarea o functie (Function Overloading) sau un operator (Operator Overloading). | ||
- | |||
- | **Overloading (supraincarcarea)** = posibilitatea dea avea intr-o clasa mai multe metode cu acelasi nume, acestea fiind diferentiate prin semnatura | ||
- | |||
- | Extra: Supraincarcarea are loc la compilare => Compile Time | ||
- | </note> | ||
- | <note> | ||
- | **Run Time Polymorphism** | ||
- | |||
- | Acest tip de polimorfism este obtinut cand suprascriem o functie (Function Overriding). | ||
- | |||
- | **Overriding (suprascriere)** = redefinirea metodelor care sunt existente in clasa parinte de catre clasa copil in vederea specializarii lor. Metodele in clasa parinte nu sunt afectate/modificate. | ||
- | Observatie: | ||
- | * Se pot suprascrie doar metodele vizibile pe lantul de mostenire (public, protected) | ||
- | * O metoda din clasa copil poate suprascrie o metoda din clasa parinte daca au aceeasi semnatura si acelasi tip de return | ||
- | * | ||
- | Extra: Suprascrierea are loc la executare => Run time | ||
- | |||
- | (Exemplu in cadrul Laboratorului 6 cand invatam despre Derivare) | ||
- | </note> | ||
- | ==== Exemplu ==== | ||
- | <code c++ CompileTime.cpp> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | class OOP { | ||
- | public: | ||
- | void fct (int x) { //Avem un parametru de tip int | ||
- | cout << "x = " << x << endl; | ||
- | } | ||
- | void fct (float x) { //Avem un parametru de tip float | ||
- | cout << "x = " << x << endl; | ||
- | } | ||
- | void fct (int x, int y) { //Avem doi parametri | ||
- | cout << "x + y = " << x + y << endl; | ||
- | } | ||
- | }; | ||
- | |||
- | int main () { | ||
- | OOP laborator; | ||
- | | ||
- | OOP.fct(10); //o sa fie apelata prima metoda (cea care are parametrul de tip int) | ||
- | OOP.fct(9.99); //o sa fie apelata a doua metoda (cea care are parametrul de tip float) | ||
- | OOP.fct(5,5); //o sa fie apelata a treia metoda (cea cu doi parametri) | ||
- | | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | <note>Observam ca, desi au acelasi nume, **semnatura** metodelor difera doar prin **tipul** si **numarul** parametrilor.</note> | ||
- | |||
- | ===== Agregare ===== | ||
- | <note important>**Agregarea** = procedeul prin care se creeaza un nou tip de date (o noua clasa) folosind tipuri de date (clase) existente | ||
- | |||
- | **Relatia de agregare** = o relatie intre clase de tipul "has a", "has many"</note> | ||
- | ==== De ce? ==== | ||
- | <note tip>**Reutilizarea codului duplicat in dezvoltarea de aplicatii** (code reuse) | ||
- | |||
- | Daca reutilizam codul => evitam sa avem cod duplicat, ceea ce ne conduce la: | ||
- | * **eficienta** in materie de timp, proiectul o sa fie implementat mai repede | ||
- | * lizibilitatea codului (**arhitectura robusta**, **modularizare**) | ||
- | </note> | ||
- | |||
- | ==== Scurt exemplu ==== | ||
- | |||
- | <code c++> | ||
- | //cod | ||
- | class A { | ||
- | //lista atribute; | ||
- | //lista metode; | ||
- | }; | ||
- | class B{ | ||
- | private: | ||
- | A a; | ||
- | //alte atribute si metode | ||
- | }; | ||
- | </code> | ||
- | <note> **Ce observam?** | ||
- | * clasa B are un atribut de tipul clasei A | ||
- | * clasa B nu are acces direct la atributele din A, dar poate sa utilizeze functiile acesteia pentru a realiza operatiile de interes | ||
- | * se protejeaza incapsularea datelor din A | ||
- | * se reutilizeaza codul deja implementat din A | ||
- | </note> | ||
- | ==== Exemplu amanuntit ==== | ||
- | <code c++> | ||
- | #include <iostream> | ||
- | using namespace std; | ||
- | class A { | ||
- | private: | ||
- | int atr; | ||
- | public: | ||
- | A():atr(0) { /*pana sa se intre in implementarea constructorului, | ||
- | se apeleaza pseudoconstructorul pentru atributul atr | ||
- | care aloca spatiu ptr atr si il initializeaza cu 0 */ | ||
- | cout << "Constructor fara parametri" << endl; | ||
- | } | ||
- | A(const A &x):atr(x.atr) { | ||
- | /*se apeleaza pseudoconstructorul pentru atributul atr, | ||
- | care aloca spatiu ptr atr si il initializeaza cu x.atr*/ | ||
- | cout << "Constructor de copiere" << endl; | ||
- | } | ||
- | A &operator = (const A &x) { | ||
- | //nu se apeleaza pseudoconstructor pentru atr, deoarece are spatiu alocat | ||
- | cout << "Operator = " << endl; | ||
- | atr = x.atr; | ||
- | return *this; | ||
- | } | ||
- | ~A() { | ||
- | cout << "Destructor" << endl; | ||
- | } | ||
- | }; | ||
- | class B { | ||
- | private: | ||
- | A a; // B are un atribut de tip A | ||
- | /*in mod automat se genereaza un constructor fara parametri, | ||
- | constructor de copiere, operator =, destructor */ | ||
- | }; | ||
- | int main() { | ||
- | B b; | ||
- | B c(b); | ||
- | c = b; | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | <note> | ||
- | **Ce observam?** | ||
- | * pentru crearea unui obiect de tip B se apeleaza in mod implicit constructorul default din A (daca nu este specificat altul) | ||
- | * constructorul de copiere din B apeleaza automat constructorul de copiere din A (daca nu este specificat altul) | ||
- | * operatorul **=** (generat default) al lui B, il apeleaza pe cel al tipului de date A | ||
- | * destructorul clasei B apeleaza automat destructorul clasei A | ||
- | **Extra** | ||
- | |||
- | Clasa B putea sa aiba mai multe atribute de tip A, dar si alte atribute de alte tipuri precum alta_Clasa obiect, A2 obiect2 etc. | ||
- | </note> | ||
- | <note important>Tipuri de agregare: | ||
- | |||
- | * **Agregarea (Aggregation)** = este considerata **o asociere slaba (weak)**, deoarece obiectul container poate exista si in absenta obiectelor agregate. (Exemplu: O biblioteca poate exista si fara carti) | ||
- | |||
- | * **Compunerea (Composition)** = este o **agregare puternica (strong)**, deoarece existenta unui obiect este dependenta de existenta unui alt obiect. La disparitia obiectelor obtinute prin compunere, existenta obiectului container inceteaza. (Exemplu: Daca o carte nu contine pagini, aceasta nu poate exista) | ||
- | </note> | ||
- | <note>**Initializarea** obiectelor intr-o clasa agregata poate fi facuta in 3 momente de timp diferite: | ||
- | * la **definirea** obiectului (inaintea constructorului folosind o valoare initiala sau blocuri de initializare) | ||
- | * **in cadrul constructorului** | ||
- | * **inainte de folosire (lazy initialization)** | ||
- | </note> |