Differences

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

Link to this comparison view

poo-is:laboratoare:06 [2020/09/29 22:43]
alexandru.ionita99 [5.1. Diferente]
— (current)
Line 1: Line 1:
-======== Laboratorul 06: Derivare ======== 
-In cadrul acestui laborator, vom aprofunda inca un concept important al Programarii Orientate pe Obiecte, **derivarea**. Pe parcursul acestui laborator vom aborda notiuni precum: **mostenire,​ ierarhie de clase, modificatori de acces, shadowing, upcasting, pragma once, redefinire**. 
  
-Ca referinte externe, recomandam urmatorul capitol din [[https://​discourse-production.oss-cn-shanghai.aliyuncs.com/​original/​3X/​2/​3/​2380479dcb8e375425884a10da232730bbc7f88d.pdf|Absolute C++]]: 
-  * Capitolul 14 (Chapter 14: Inheritance,​ pag. 613-661) 
- 
- 
-===== 1. Introducere ===== 
-<note important>​ 
-**Derivarea** este o relatie intre clase de tipul "is a" / "is many". 
-</​note>​ 
-<note important>​ **Mostenirea** (numita si **derivare**) este un mecanism de refolosire a codului. Totodata, ofera posibilitatea de a defini o clasa care ,,​extinde"​ o clasa existenta (la codul de baza din clasa existenta adaugandu-se noi atribute si/sau metode). </​note>​ 
- 
-==== 1.1. Exemplu ==== 
-<code c++> 
-//cod 
-class Produs { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-}; 
- 
-class Electrocasnic { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-        int durata_garantie;​ 
-}; 
- 
-class Aliment { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-        int durata_expirare;​ 
-}; 
- 
-class Jucarie { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-        int varsta_recomandata[2];​ 
-}; 
- 
-class TV { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-        int durata_garantie;​ 
-        double diagonala; 
-}; 
- 
-class Masina_cafea { 
-    private: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-        int durata_garantie;​ 
-        char *tip; //expresor, filtru 
-}; 
-/* iar exemplele pot continua */ 
-//cod 
-</​code>​ 
-==== 1.2. Ce observam? ==== 
-Putem observa ca avem o clasa de baza, **Produs**, si ca putem distinge mai multe categorii de produse. Aceste categorii de produse sunt niste clase care, pe langa faptul ca au comportamentul clasei Produs, au in plus atribute si comportamente specifice (**clase specializate**). ​ 
- 
-De exemplu: Masina_cafea si TV-ul sunt electrocasnice,​ dar acestea au comportamente (si atribute) specifice. (Un TV nu poate sa faca cafea, iar masina_cafea nu difuzeaza stirile de la ora 17:00) 
- 
-<note warning>​**Problema intampinata:​** Am __rescris__ foarte mult cod. Cum fac sa evit asta?</​note>​ 
- 
-===== 2. Derivarea ===== 
-==== 2.1. Aplicare ==== 
-<code c++> 
-//cod 
-class Produs { //clasa de baza 
-    protected: 
-        char *nume; 
-        int pret; 
-        char cod[10]; 
-}; 
- 
-class Electrocasnic : public Produs {//clasa derivata (din Produs), dar si clasa de baza 
-    protected: 
-        int durata_garantie;​ 
-}; 
- 
-class Aliment : public Produs{//​clasa derivata (din Produs) 
-    private://​daca implementam o clasa care sa fie derivata din clasa ALiment, folosim protected ​ 
-        int durata_expirare;​ 
-}; 
- 
-class Jucarie : public Produs{//​clasa derivata (din Produs) 
-    private://​daca implementam o clasa care sa fie derivata din clasa Jucarie, folosim protected 
-        int varsta_recomandata[2];​ 
-}; 
- 
-class TV : public Electrocasnic{//​clasa derivata (din Electrocasnic) 
-    private: 
-        double diagonala; 
-}; 
- 
-class Masina_cafea : public Electrocasnic{//​clasa derivata (din Electrocasnic) 
-    private: 
-        char *tip; //expresor, filtru 
-}; 
-/* iar exemplele pot continua */ 
-//cod 
-</​code>​ 
-<note tip>Am rezolvat problema intampinata!</​note>​ 
-==== 2.2. Concluzie ==== 
-<note important>​Acum putem afirma ca: 
- 
-**Derivarea** este un procedeu prin care se creeaza un nou tip de date (o noua clasa) folosind o clasa existenta, mai exact, adaugam un cod nou (atribute noi, metode noi) codului deja existent => **se preia interfata clasei de baza**. 
- 
-In clasa derivata sunt mostenite toate atributele si metodele clasei de baza si le pot accesa direct daca sunt declarate public sau protected.</​note>​ 
-==== 2.3. Intrebari ==== 
-**1. Putem sa derivam mai multe clase din clasa de baza?** 
- 
-Da, se poate face acest lucru. Putem sa avem oricate clase derivate dintr-o clasa de baza si oricate niveluri de derivare (**cat timp este logic**). 
- 
-**2. Pot sa am mai multe clase parinte in acelasi timp?** 
- 
-Desigur, acest mecanism se numeste **mostenire multipla**. Exemplu: 
- 
-Avem urmatoarele clase de baza:  
-  * **Angajat** (are salariu, speram ca mare, si lucreaza la o firma) 
-  * **Student** (are note, invata la o facultate) 
-Presupunem ca studentul nostru se angajeaza, astfel se creaza o noua clasa **Student_Angajat** (are note, are salariu, lucreaza la o firma si studiaza la o facultate). Observam ca noua clasa, **Student_Angajat**,​ are ca si parinti ambele clase (Angajat, Student). 
- 
-===== 3. Modificatori de acces (permisiuni) ===== 
-<​note>​In C++ avem 3 modificatori de acces: 
-  * **public** 
-  * **private** 
-  * **protected** 
-</​note>​ 
-<note important>​**Public** 
- 
-Tot ce este declarat public este vizibil pentru toate lumea. Daca avem metode sau atribute publice intr-o clasa, le putem apela folosind operatorul "​**.**"​. 
- 
-**Private** 
- 
-Tot ce este declarat private este vizibil doar la nivel de clasa. Daca avem atribute declarate ca fiind publice, acestea sunt vizibile doar in clasa respectiva. De aceea, folosim metode prin care extragem valorile atributelor private. ​ 
- 
-**Protected** 
- 
-Tot ce este declarat protected este inaccesibil din exteriorul clasei, exceptie facand doar clasele derivate dintr-o clasa cu atribute protected. Acestea, clasele derivate, pot sa acceseze atributele protected fara a fi nevoie de a folosi metode speciale care returneaza valori. 
-</​note>​ 
-==== 3.1. Exemplul 1 ==== 
-<code c++ Public.cpp>​ 
-#include <​iostream>​ 
-using namespace std; 
- 
-class Cerc { 
-    public: 
-        double raza; 
-        double arie_cerc() { 
-            return 3.14 * raza * raza; 
-        } 
-}; 
- 
-int main() { 
-    //Exemplu public 
-    Cerc circle; 
-    circle.raza = 6.5; 
-    cout << "Raza cercului este: "<<​ circle.raza << endl; //o sa afiseze 6.5 
-    cout << "Aria cercului este: "<<​ circle.arie_cerc() << endl; //o sa afiseze aria cercului 
-    ​ 
-    return 0; 
-} 
-</​code>​ 
-<code c++ Private.cpp>​ 
-#include <​iostream>​ 
-using namespace std; 
- 
-class Cub { 
-     ​private:​ 
-         int latura; 
-     ​public: ​ 
-         int arie_cub () { 
-             ​return 6 * latura * latura; 
-         } 
-}; 
- 
-int main() { 
-    //Exemplu private 
-    Cub cube; 
-    cube.latura = 5; /*incercam sa ii atribuim o valoare atributului ​ 
-                     ​latura din clasa Cub, inafara clasei 
-                     nu putem face acest lucru fara o metoda specifica */ 
-    cout << "Aria cubului este: " << cube.arie_cub() << endl; //o sa afiseze o eroare 
- 
-    return 0; 
-} 
-</​code>​ 
-<code c++ Protected.cpp>​ 
-#include <​iostream>​ 
-using namespace std; 
- 
-class Parinte { 
-    protected: 
-        int id_clasa; 
-}; 
- 
-class Copil : public Parinte { 
-    public: 
-        void setId (int id) { 
-            id_clasa = id; 
-        } 
-        void print_id () { 
-            cout << "​Id_copil:​ " << id_clasa; 
-        } 
-}; 
- 
-int main () { 
-    //Exemplu protected 
-    Copil obiect; 
-    obiect.setId(77);​ 
-    obiect.print_id();​ //o sa afiseze ,,Id copil: 77" 
-    ​ 
-    return 0; 
-} 
-</​code>​ 
-==== 3.2. Exemplul 2 ==== 
-|    Indicator de vizibilitate ​   ^    Accesibilitate ​   ^    Zona    ^ 
-^    public ​   |accesibil ​     |din exteriorul ierahiei ​   | 
-^    :::       ​|accesibil ​     |din clasele derivate ​       | 
-^    protected ​    ​|inaccesibil ​   |din exteriorul ierarhiei ​   | 
-^    :::       ​|accesibil ​     |din clasele derivate si orice alte clase derivate din clasele derivate ​   | 
-^    private ​  ​|inaccesibil ​   |din exteriorul ierarhiei ​   | 
-^    :::       ​|inaccesibil ​   |din clasele derivate ​       | 
-<code c++> 
-class A { 
-    public: 
-        int x; 
-    protected: 
-        int y; 
-    private: 
-        int z; 
-}; 
-class B : public A {  
-    // x este public ​ 
-    // y este protected ​ 
-    // z nu este accesibil din B  
-};  
-  ​ 
-class C : protected A {  
-    // x este protected ​ 
-    // y este protected ​ 
-    // z nu este accesibil din C  
-};  
-  ​ 
-class D : private A {  
-    // x este private ​ 
-    // y este private ​ 
-    // z nu este accesibil din D  
-};  
-</​code>​ 
-<note important>​Derivarea este implicit privata (class Derivata : Baza). ​ 
- 
-Insa, vrem sa fie (aproape mereu) publica (class Derivata: public Baza). ​ 
- 
-Rareori este protected (class Derivata : protected Baza).</​note>​ 
- 
- 
-===== 4. Comportamentul metodelor din clasa derivata ===== 
-==== 4.1. Constructorii ==== 
- 
-  * Din punct de vedere al ordinii, prima oara se apeleaza constructorul / constructorii din clasa de baza si apoi cei din clasa derivata. 
-  * In cazul in care clasa derivata **nu are niciun constructor declarat**, se genereaza unul default care va apela constructorul fara parametrii al clasei de baza (**trebuie sa existe**) 
-  * Daca in clasa derivata nu avem constructor de copiere, se genereaza unul automat care il apeleaza pe cel din clasa de baza (similar ca la punctul anterior) 
-  * Daca implementam un constructor pentru clasa derivata care nu apeleaza un constructor al clasei de baza, atunci, by default, se va apela constructorul fara parametrii al clasei de baza (**acesta trebuie sa existe obligatoriu**,​ daca nu => eroare) 
- 
-==== 4.2. Operatorul = ==== 
- 
-  *Daca este generat automat, sa va apela operatorul = din clasa (clasele) de baza. Insa, daca este implementat de noi, trebuie sa ne asiguram ca are acest comportament (de a apela operatorul = din clasa de baza). 
- 
-==== 4.3. Destructorul ==== 
- 
-  *Din punct de vedere al ordinii, prima oara se apeleaza destructorul din clasa derivata si apoi cel din clasa de baza. 
- 
-  *Nu se face o apelare explicita deoarece **exista un singur destructor** in clasa de baza. 
- 
-<note important>​Contructorii,​ destructorii si functiile friend **nu se mostenesc** in clasele derivate. 
- 
-In general, operatorii implementati ca functii membre **se mostenesc**. O exceptie este operatorul de atribuire, acesta **nu se mosteneste**. 
- 
- 
-Functiile friend pot accesa atributele si metodele **protected/​public/​private** din clasa cu care sunt prietene.</​note>​ 
-== Observatii == 
-<​note>​Functiile membre statice se comporta ca orice functie membra, sunt mostenite in clasa derivata. ​ 
- 
-**Nu pot fi virtuale **(notiune aprofundata in cadrul laboratorului 7).</​note>​ 
-===== 5. Agregare vs derivare ===== 
-Amandoua mecanismele reutilizeaza codul scris pentru o clasa de baza/simpla intr-o alta clasa mai complexa. In ambele cazuri se folosesc liste de initializare pentru constructori pentru a crea obiectele de baza (chiar daca apelul difera prin sintaxa). 
- 
-==== 5.1. Diferente ==== 
-== Agregarea == 
-  * folosita cand se doreste reutilizarea unui tip de date A pentru generarea altui tip de date B (**fara a prelua interfata lui A**) 
-  * se integreaza in clasa mai complexa un atribut (sau mai multe) de tipul clasei mai simple 
-  * utilizatorii noii clase vor vedea doar interfata acesteia 
-  * nu va mai fi de interes interfata clasei de baza 
-== Derivare == 
-  * folosita cand se doreste **preluarea interfetei** clasei de baza 
-  * utilizatorul va vedea atat interfata clasei de baza cat si a celei derivate 
- 
-<​note>​Nu exista nicio diferenta in termeni de: memorie ocupata si durata de executie.</​note>​ 
- 
-== Avantajele derivarii == 
-<note tip>​Putem sa adaugam cod nou fara a introduce bug-uri in codul existent. 
- 
-Erorile se gasesc in codul nou => mai usor de cautat/​gasit. 
- 
-Clasele sunt clar separate. (Codul poate fi refolosit si in alte locuri fara a avea erori)</​note>​ 
- 
-===== 6. Mostenire multipla ===== 
-==== 6.1. Exemplu ==== 
-<code c++> 
-#include <​iostream>​ 
-using namespace std; 
-class Baza1 { 
-    protected: 
-        int atribut1; 
-    public: ​ 
-        Baza1(int i = 0) : atribut1(i){} 
-        void set_atribut1(int i) { 
-            atribut1 = i; 
-        } 
-        void afisare_atribut1() { 
-            cout << "​\nAtribut1 = " << atribut1 << endl; 
-        } 
-}; 
-class Baza2 { 
-    protected: 
-        int atribut2; 
-    public: 
-        Baza2(int i = 0) : atribut2(i){} 
-        void set_atribut2(int i) { 
-            atribut2 = i; 
-        } 
-        void afisare_atribut2() { 
-            cout << "​\nAtribut2 = " << atribut2 << endl; 
-        } 
-}; 
-class Derivata : public Baza1, public Baza2 { 
-    private: 
-        int atribut3; 
-    public: ​ 
-        Derivata() {} 
-        Derivata(int a1, int a2, int a3) : Baza1(a1),​Baza2(a2),​atribut3(a3){} 
-        void set_atribut3(int i) { 
-            atribut3 = i; 
-        } 
-        void set_atribute(int a1, int a2, int a3) { 
-            atribut1 = a1; 
-            atribut2 = a2; 
-            atribut3 = a3; 
-        } 
-        void afisare_atribute() { 
-            cout << "​\nAtribut1 = " << atribut1 << "​\nAtribut2 = " 
-             <<​ atribut2 << "​\nAtribut3 = " << atribut3 << endl; 
-        } 
-}; 
-int main() { 
-    Derivata obiect(1,​2,​3);​ 
-    obiect.afisare_atribute();​ 
-    cout << "​\n"​ << "​________________"​ << endl; 
-    obiect.set_atr1(5);​ 
-    obiect.set_atr2(6);​ 
-    obiect.set_atr3(7);​ 
-    obiect.afisare_atribut1();​ 
-    obiect.afisare_atribute();​ 
-    ​ 
-    return 0; 
-} 
-</​code>​ 
-<note warning>​Cand utilizam mostenirea multipla putem intampina urmatoarele probleme: 
- 
-  *atribute si metode cu acelasi nume in clasele de baza 
-  *derivare dubla indirecta din clasa de baza 
-  *altele 
-</​note>​ 
-===== 7. Shadowing, Upcasting ===== 
-== Shadowing == 
-<code c++> 
-class A { 
-    protected: ​ 
-        int atr; 
-}; 
- 
-class B : public A { 
-    private: 
-        //am mostenit si atributul ,,atr" din clasa A 
-        int atr; // are priotate fata de atributul cu acelasi nume din A 
-    public: 
-        void set_atr (int i, int j) { 
-            A::atr = i; //pentru a avea acces la atributul ,,atr" din A 
-                        //il apelam folosind A:: 
-            atr = j; 
-        } 
-}; 
-</​code>​ 
-== Upcasting == 
-Daca o clasa este derivata dintr-o alta clasa de baza (relatie "is a") => obiectele de tipul clasei derivate, sunt, in acelasi timp, si obiecte de tipul clasei de baza. 
-<note important>​Acest fapt ne permite urmatoarea atribuire: 
- 
-obiect_clasa_baza = obiect_clasa derivata (**upcasting**,​ conversie la tipul obiectului de baza)</​note>​ 
-Daca facem acest lucru (obiect_clasa_baza = obiect_clasa derivata) vom pierde informatiile suplimentare stocate in **obiect_clasa_derivata**. 
-<note important>​Este permisa atribuirea in care se ,,pierd date". Nu sunt permise cele in care nu se stie cu ce sa se completeze campurile suplimentare (obiect_clasa derivata = obiect_clasa_baza).</​note>​ 
-Putem face acest lucru posibil (obiect_clasa_derivata = obiect_clasa_baza) doar daca **supradefinim** operatorul de atribuire din clasa derivata. 
- 
-<​note>​**Ce se intampla daca intr-o clasa derivata o sa redefinim o metoda din clasa de baza?** 
- 
-Putem face acest lucru prin 2 metode: 
-  *redefinire/​redefine (semnatura poate sau nu sa difere) 
-  *supraincarcare/​override (avem aceeasi semnatura si tip returnat si functia de baza era virtuala) 
-In aceste cazuri, versiunile metodelor din clasa de baza o sa fie ascunse pentru noua clasa (nu avem acces direct la functiile clasei de baza cu acelasi nume ca cele din clasa derivata). Le putem apela explicit folosind **nume_clasa_baza :: nume_functie**. 
-</​note>​ 
-===== 8. Pragma Once ===== 
-In cazul in care avem ierarhii complicate de clase, trebuie sa avem grija sa nu includem header-ul unei clase de mai multe ori in alt header sau in programul principal. 
-<note important>​Putem evita acest lucru daca folosim directiva preprocesor **#pragma once**. Aceasta se scrie la inceputul header-ului.</​note>​ 
-<code c++ header_clasa_baza.h> ​ 
-#pragma once //include aceasta sursa (header_clasa_baza.h) o singura data in momentul compilarii 
-#include <​iostream>​ 
-using namespace std; 
-class Baza { 
-    protected: 
-        int atribut; 
-    public: ​ 
-        Baza(int i):​atribut(i){} 
-}; 
-</​code>​ 
-**Alternativa** 
-<code c++ header_clasa_baza.h>​ 
-#ifndef HEADER_CLASA_BAZA 
-    #define HEADER_CLASA_BAZA 
-    #include <​iostream>​ 
-    using namespace std; 
-    class Baza { 
-        protected: 
-            int atribut; 
-        public: ​ 
-            Baza(int i):​atribut(i){} 
-    }; 
-#endif 
-</​code>​ 
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