Laboratorul 05: Polimorfism. Agregare

In cadrul laboratorului 5 al Programarii Orientate pe Obiecte, vom aprofunda unul dintre cele 4 principii ale POO-ului, Polimorfismul. De asemenea, vom aborda conceptul de Agregare.

Cele 4 mari concepte ale Programarii Obiectuale sunt: Incapsulare, Polimorfism, Mostenire si Abstractizare. (Echivalent in engleza, pentru documentare suplimentara: Encapsulation, Polymorphism, Inheritance, Abstraction).

Ca referinte externe, recomandam urmatorul capitol din Absolute C++:

  • Capitolul 15 (Chapter 15: Polymorphism and virtual functions, pag. 661-693)

1. Polimorfism

Polimorfism = abilitatea unei functii cu un anumit nume sa aiba comportamente diferite, in functie de parametrii de intrare.

In C++, avem mai multe tipuri de polimorfism:

  • Compile Time Polymorphism
  • Run Time Polymorphism

Compile Time Polymorphism

Acest tip de polimorfism este obtinut prin supraincarcarea unei functii (Function Overloading) sau al unui operator (Operator Overloading).

Overloading (supraincarcarea) = posibilitatea de a avea intr-o clasa mai multe metode cu acelasi nume, fiind diferentiate prin semnatura.

Extra: Supraincarcarea are loc la compilare ⇒ Compile Time

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 7 cand invatam despre Functii Virtuale)

Semnatura unei metode consta in:

  • numele metodei
  • numarul si tipul parametrilor

1.1. Exemplu

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;
}

Observam ca, desi au acelasi nume, semnatura metodelor difera doar prin tipul si numarul parametrilor.

2. Agregare

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”

2.1. De ce?

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)

2.2. Scurt exemplu

//cod
class A {
    //lista atribute;
    //lista metode;
};
class B{
    private:
    A a;
    //alte atribute si metode
};

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

2.3. Exemplu amanuntit

#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;
}

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.

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)

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) (Exemplu in Cursul 6, pag 37/47, observam ca s-a facut alocarea de spatiu pentru Pagina din vectorul de pagini, dar nu s-au dat valori pentru atributele din Pagina)

poo-is-aa/laboratoare/05.txt · Last modified: 2024/08/14 20:10 (external edit)
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