C++ POO Lab Lucrarea 4: Diferență între versiuni

De la WikiLabs
Jump to navigationJump to search
Fără descriere a modificării
Linia 8: Linia 8:
* Metode pur virtuale
* Metode pur virtuale


= Moștenire =
= Moștenirea =


Moștenirea este mecanismul prin care o clasă preia structura (câmpuri) și comportamentul (metodele) unei alte (sau mai multor) clase, la care poate adaugă elemente specifice
== Introducere ==
Moștenirea este mecanismul prin care o clasă preia structura (câmpurile) și comportamentul (metodele) unei alte (sau mai multor) clase, la care poate adăuga alți membri specifici.


Clasa de bază = clasa de la care se preia structura si comportamentul
Clasa de la care se preiau membrii se numește:
* clasă de bază
* superclasă


Clasa derivata = clasa care preia structura si comportamentul
Clasa nouă, care preia membrii de la clasa de bază se numește:
* clasă derivată
* clasă extinsă
* subclasă


În C++, moștenirea poate fi multiplă, în sensul ca o clasa derivata poate moșteni mai multe clase de baza. Nu în toate limbajele exista acest concept (în Java de exemplu, există doar moștenire simplă)
În C++, moștenirea poate fi multiplă, în sensul ca o clasa derivată poate moșteni mai multe clase de bază. Nu în toate limbajele există acest concept (în Java. de exemplu, există doar moștenire simplă - adică o clasă poate moșteni o singură altă clasă).


'''Avantaje''':
Utilitatea moștenirii în programarea orientată pe obiecte este:
* reutilizarea codului existent fără modificarea acestuia;
* extinderea a unei clase deja scrise, fără a fi necesara recompilarea ei;
* utilizarea polimorfismului în timpul execuției, prin folosirea metodelor virtuale.


* reutilizare cod
== Exemplu ==
* extensie a unei clase deja scrise, fără a fi necesara recompilarea ei
<syntaxhighlight lang="C++">
* utilizarea polimorfismului în timpul execuției, prin folosirea funcțiilor virtuale
 
class Animal {
    std::string mName;
    std::string mColor;
    int mAge;
    bool mHasFeathers;
 
public:
    void makeSound() const {
        printf("Animal %s makes a sound!\n", mName.c_str());
    }


Exemplu (pentru o clasa mostenita din clasa Animal de mai sus)
    // this is a getter method
    std::string getName() const {
        return mName;
    }


<syntaxhighlight lang="C++">
    // this is a setter method
class Pisica : public Animal {
    void setAge(int age) {
        if(age > 0) {
            mAge = age;
        }
    }
};
 
class Cat : public Animal {
    unsigned int mLives;
public:
public:
     Pisica() : Animal() // constructorul implicit Animal este chemat explicit
     Cat() : mLives(9) {
    {  
     }
      mHasFeathers = false;
};  
     } // campul mHasFeathers este protected in Animal, deci poate fi accesat de Pisica
}; // nu uitati de ; la sfarsitul unei clase
</syntaxhighlight>
</syntaxhighlight>


Relativ la specificatorul de acces declarat intre clasa derivata si clasa de baza  (class Pisica : '''public''' Animal) avem si posibilitatea de a folosi '''private''':
În exemplul de mai sus, clasa <code>Cat</code> preia toți membrii din clasa <code>Animal</code> și adaugă un câmp <code>mLives</code> și un constructor fără argumente. Se observă, de asemenea, că moștenirea a fost declarată publică. Există două timpuri de moșteniri:
# publică
# privată
 
Relativ la tipul de moștenire, mai jos este prezentat modul în care se preiau membrii din clasa de bază:
{| class="wikitable"
{| class="wikitable"
|+ Accesul asupra membrilor moșteniți
|+ Accesul asupra membrilor moșteniți

Versiunea de la data 12 aprilie 2022 20:29

Această lucrare are ca scop familiarizarea cu următoarele noțiuni:

  • Moștenirea
  • Ascunderea metodelor
  • Polimorfismul
  • Metode virtuale
  • Suprascrierea metodelor
  • Metode pur virtuale

Moștenirea

Introducere

Moștenirea este mecanismul prin care o clasă preia structura (câmpurile) și comportamentul (metodele) unei alte (sau mai multor) clase, la care poate adăuga alți membri specifici.

Clasa de la care se preiau membrii se numește:

  • clasă de bază
  • superclasă

Clasa nouă, care preia membrii de la clasa de bază se numește:

  • clasă derivată
  • clasă extinsă
  • subclasă

În C++, moștenirea poate fi multiplă, în sensul ca o clasa derivată poate moșteni mai multe clase de bază. Nu în toate limbajele există acest concept (în Java. de exemplu, există doar moștenire simplă - adică o clasă poate moșteni o singură altă clasă).

Utilitatea moștenirii în programarea orientată pe obiecte este:

  • reutilizarea codului existent fără modificarea acestuia;
  • extinderea a unei clase deja scrise, fără a fi necesara recompilarea ei;
  • utilizarea polimorfismului în timpul execuției, prin folosirea metodelor virtuale.

Exemplu

class Animal {
    std::string mName;
    std::string mColor;
    int mAge;
    bool mHasFeathers;

public:
    void makeSound() const {
        printf("Animal %s makes a sound!\n", mName.c_str());
    }

    // this is a getter method
    std::string getName() const {
        return mName;
    }

    // this is a setter method
    void setAge(int age) {
        if(age > 0) {
            mAge = age;
        }
    }
};

class Cat : public Animal {
    unsigned int mLives;
public:
    Cat() : mLives(9) {
    }
};

În exemplul de mai sus, clasa Cat preia toți membrii din clasa Animal și adaugă un câmp mLives și un constructor fără argumente. Se observă, de asemenea, că moștenirea a fost declarată publică. Există două timpuri de moșteniri:

  1. publică
  2. privată

Relativ la tipul de moștenire, mai jos este prezentat modul în care se preiau membrii din clasa de bază:

Accesul asupra membrilor moșteniți
Protecția in clasa de baza Modif de acces utilizat în lista claselor de bază Dreptul de acces în clasa derivată
public public public
private public inaccesibil
protected public protected
public private private
private private inaccesibil
protected private private

Polimorfism

Polimorfismul parametric – mecanismul prin care putem defini o metodă cu același nume în aceași clasă, funcții care trebuie să difere prin numărul și/sau tipul parametrilor și/sau ordinea lor. Selecția funcției se realizează la compilare (legarea timpurie (early binding)). Se numește foarte des supraîncarcare pentru a evita confuzia cu polimorfismul de moștenire.

Polimorfismul de moștenire – mecanismul prin care o metodă din clasa de bază este redefinită cu aceiași semnatură (return value, nume, parametri) în clasele derivate. Selecția funcției se va realiza la execuție (legare întârziată == late binding). Acesta este sensul în care se folosește cel mai des, și deci sensul in care va fi interpretat cuvantul polimorfism (daca nu este specificat de ce tip este)

Avantaje:

  • clasa de baza si pointerul de tip clasa de baza pot fi scrise in codul sursa al unei biblioteci compilate cu mult timp inaintea compilarii clasei derivate (eg. ani de zile)
  • pointerul de tip clasa de baza poate referi orice obiect, de orice tip clasa derivata a clasei de baza (și isi poate schimba dinamic valoarea, la execuție)
#include <iostream>

class Animal {
private:
    std::string mName;
    std::string mColor;
    int mAge;

protected:
    bool mHasFeathers; // accesibil din eventualele clase derivate

public:
    virtual void makeSound() {
        std::cout << "Animal " << mName << " makes a sound!" << std::endl;
    }

    void setName(std::string str) {
        mName = str;
    }

    // this is a getter method
    std::string getName() {
        return mName;
    }

    // this is a setter method
    void setAge(int age) {
        if(age > 0) {
            mAge = age;
        }
    }
};

class Pisica : public Animal {
public:
    Pisica() : Animal() { mHasFeathers = false; }

    // cuvantul '''override''' este optional dar recomandat si produce o eroare de compilare daca metoda makeSound din Pisica nu suprascrie metoda
    // makeSound din Animal din cauza unei erori de sintaxa (eg. nume usor schimbat, alt return type, alti parametri / ordine sau tip al lor
    void makeSound() override {
        std::cout << "Pisica " << getName() << " makes a sound!" << std::endl;
    }

    // metoda din clasa Pisica ascunde implementarea din clasa Animal, dar nu o suprascrie polimorfic pentru ca setAge nu este virtuala in Animal
    void setAge(int age) {
        if (age >= 20) std::cout << "WARN: Longeviva pisica... sigur nu este o greseala?" << std::endl;

        Animal::setAge(age); // chemare setAge din clasa de baza Animal
    }
};

int main() {

    std::cout << std::endl << "Demo Animal:" << std::endl;
    Animal *pAnimal;
    Animal un_animal;
    un_animal.setName("TestoasaNinja");

    un_animal.makeSound(); // metoda makeSound din clasa Animal
    un_animal.setAge(99); // metoda setAge din clasa Animal

    pAnimal = &un_animal;
    pAnimal->makeSound(); // metoda makeSound din clasa Animal
    pAnimal->setAge(99); // metoda setAge din clasa Animal

    std::cout << std::endl << "Demo Pisica:" << std::endl;
    Pisica tom;
    tom.setName("Tom");
    tom.makeSound(); // metoda makeSound din clasa Pisica
    tom.setAge(20); // metoda setAge din clasa Pisica

    Pisica *pPisica;
    pPisica = &tom;
    pPisica->makeSound(); // metoda makeSound din clasa Pisica
    pPisica->setAge(20); // metoda setAge din clasa Pisica

    // demonstratie POLIMORFISM (de mostenire):
    std::cout << std::endl << "Demo Polimorfism (de mostenire):" << std::endl;
    pAnimal = &tom; // pointer de tip clasa de baza Animal, care contine o adresa de tip clasa derivata, Pisica
    pAnimal->makeSound(); // ATENTIE: medoda makeSound din clasa Pisica (makeSound ESTE virtuala in clasa de baza Animal)
    pAnimal->setAge(20); // ATENTIE: metoda setAge din clasa Animal (setAge NU este virtuala in clasa de baza Animal)

    // pPisica = &un_animal; // eroare de compilare: invalid conversion from ‘Animal*’ to ‘Pisica*’ [-fpermissive]

    return 0;
}

Secventa de mai sus afiseaza:

Demo Animal:
Animal TestoasaNinja makes a sound!
Animal TestoasaNinja makes a sound!

Demo Pisica:
Pisica Tom makes a sound!
WARN: Longeviva pisica... sigur nu este o greseala?
Pisica Tom makes a sound!
WARN: Longeviva pisica... sigur nu este o greseala?

Demo Polimorfism (de mostenire):
Pisica Tom makes a sound!

Functii/Metode virtuale pure, clase abstracte

- o functie/metoda virtuala pura este o functie/metoda care nu are corp (body), nici macar gol

virtual std::string toString() {}; // nu este o functie/metoda virtuala pura, are corp

virtual std::string toString() = 0; // este o functie/metoda virtuala pura

O clasa abstracta este o clasa care contine cel putin o metoda virtuala pura, si deci nu se poate instantia (nu exista obiecte de tip clasa

Exemplu (pentru o clasa mostenita din clasa Animal de mai sus)

class Pisica : public Animal {
public:
    Pisica() : Animal() // constructorul implicit Animal este chemat explicit
    { 
      mHasFeathers = false;
    } // campul mHasFeathers este protected in Animal, deci poate fi accesat de Pisica 
}; // nu uitati de ; la sfarsitul unei clase