4. Zßklady OOP IV
  1. Nßsledujφcφ program je nß╣ prvnφ p°φklad, kter² pou╛φvß ochranu dat. Jestli╛e jsou datovΘ slo╛ky a metody t°φdy p°edka voln∞ dostupnΘ i ve t°φdßch potomk∙, dßvß to programßtor∙m v∞t╣φ mo╛nosti p°i odvozovßnφ nov²ch t°φd. To zajistφme vlo╛enφm klφΦovΘho slova protected p°ed deklaraci t∞chto slo╛ek. Toto bylo provedeno u deklarace t°φdy vozidlo. U obou odvozen²ch t°φd je p°ed deklaraci slo╛ek p°idßno klφΦovΘ slovo private (implicitn∞ jsou v╣echna data t°φdy soukromß a p°idßnφ tohoto klφΦovΘho slova tedy nemß ╛ßdn² v²znam). Souhrnn∞ m∙╛eme popsat v²znam t∞chto klφΦov²ch slov takto:
  2. Po provedenφ t∞chto zm∞n jsou v metodßch odvozen²ch t°φd dostupnΘ i slo╛ky z t°φdy p°edka. Vyzkou╣ejte.
    class vozidlo {
    protected:
      int kola;
      float vaha;
    public:
      void inicializace(int nova_kola, float nova_vaha);
      int ziskej_kola(void){return kola;}
      float ziskej_vahu(void){return vaha;}
      float vaha_na_kolo(void){return vaha/kola;}
    };
    class automobil : public vozidlo {
    private:
      int osob;
    public:
      void inicializace(int nova_kola, float nova_vaha, int lidi = 4);
      int pasazeru(void){return osob;}
    };
    class nakladni : public vozidlo {
    private:
      int osob;
      float naklad;
    public:
      void inic_nakl(int kolik = 2, float max_naklad = 12000.0);
      float vykonost(void);
      int pasazeru(void){return osob;}
    };
    void vozidlo::inicializace(int nova_kola, float nova_vaha){
      kola = nova_kola;
      vaha = nova_vaha;
    }
    void automobil::inicializace(int nova_kola, float nova_vaha, int lidi){
      osob = lidi;
      vozidlo::inicializace(nova_kola, nova_vaha);
    }
    void nakladni::inic_nakl(int kolik, float max_naklad){
      osob = kolik;
      naklad = max_naklad;
    }
    float nakladni::vykonost(void){
      return naklad / (naklad + ziskej_vahu());
    }
    int main(int argc, char **argv)
    {
      vozidlo unicykl;
      unicykl.inicializace(1, 7.5);
      cout << "Unicykl mß " << unicykl.ziskej_kola() << " kol.\n";
      cout << "Zßt∞╛ na kolo unicyklu je " << unicykl.vaha_na_kolo()<< endl;
      cout << "Vßha unicyklu je " << unicykl.ziskej_vahu() << " kg.\n";
      automobil sedan;
      sedan.inicializace(4, 1500.0, 5);
      cout << "Sedan mß " << sedan.pasazeru() << " osob.\n";
      cout << "Sedan mß zßt∞╛ " << sedan.vaha_na_kolo() << " kg na kolo.\n";
      cout << "Sedan vß╛φ " << sedan.ziskej_vahu() << " kg.\n";
      nakladni tatra;
      tatra.inicializace(2, 7500.0);
      tatra.inic_nakl(1, 7000.0);
      cout << "Vßha tatry je " << tatra.ziskej_vahu() << " kg.\n";
      cout << "V²konnost tatry je "<<100.0*tatra.vykonost()<<" procent.\n";
      return 0;
    }
    Specifikßtor p°φstupu uveden² p°ed identifikßtorem t°φdy p°edka urΦuje p°φstup ke zd∞d∞n²m slo╛kßm t°φdy. Pokud t°φda deklaruje svΘho p°edka se specifikacφ public, jsou p°φstupovß prßva ke zd∞d∞n²m ve°ejn²m a chrßn∞n²m slo╛kßm stejnΘ jako ve t°φd∞ p°edka. SoukromΘ slo╛ky jsou nep°φstupnΘ. Deklarujeme-li p°edka se specifikacφ protected, omezφ se p°φstupovß prßva k zd∞d∞n²m ve°ejn²m slo╛kßm. Slo╛ky, kterΘ byly v p°edkovi ve°ejnΘ nebo chrßn∞nΘ, budou v potomkovi vystupovat jako chrßn∞nΘ a slo╛ky, kterΘ byly soukromΘ, z∙stßvajφ nep°φstupnΘ. Deklarujeme-li p°edka se specifikacφ private, budou v╣echny zd∞d∞nΘ ve°ejnΘ a chrßn∞nΘ slo╛ky pova╛ovßny za soukromΘ (soukromΘ slo╛ky p°edka jsou nep°φstupnΘ). Pokud u p°edka nedeklarujeme specifikaci p°φstupu, platφ implicitnφ specifikace, kterß je stejnß jako implicitnφ specifikace p°φstupu k jednotliv²m slo╛kßm: u t°φdy typu struct je to specifikace public a u t°φd typu class specifikace private.
  3. V dal╣φ verzi tohoto programu jsou pou╛ity konstruktory. T°φda vozidlo mß konstruktor inicializujφcφ polo╛ky kola a vaha na implicitnφ hodnoty. Ostatnφ dv∞ t°φdy majφ takΘ konstruktory, inicializujφcφ jejich polo╛ky na poΦßteΦnφ hodnoty. Jako implicitnφ hodnoty zde byly zadßny neobvyklΘ hodnoty. V hlavnφm programu byly odstran∞ny p°φkazy provßd∞jφcφ inicializaci (byly zm∞n∞ny na komentß°). Program vyzkou╣ejte.

  4. class vozidlo {
    protected:
      int kola;
      float vaha;
    public:
      vozidlo(void){kola = 7; vaha = -55.7;}
      void inicializace(int nova_kola, float nova_vaha);
      int ziskej_kola(void){return kola;}
      float ziskej_vahu(void){return vaha;}
      float vaha_na_kolo(void){return vaha/kola;}
    };
    class automobil : public vozidlo {
    private:
      int osob;
    public:
      automobil(void) {osob = 12;}
      void inicializace(int nova_kola, float nova_vaha, int lidi = 4);
      int pasazeru(void){return osob;}
    };
    class nakladni : public vozidlo {
    private:
      int osob;
      float naklad;
    public:
      nakladni(void) {osob = 9; naklad = 12.3;}
      void inic_nakl(int kolik = 2, float max_naklad = 12000.0);
      float vykonost(void);
      int pasazeru(void){return osob;}
    };
    void vozidlo::inicializace(int nova_kola, float nova_vaha){
      kola = nova_kola;
      vaha = nova_vaha;
    }
    void automobil::inicializace(int nova_kola, float nova_vaha, int lidi){
      osob = lidi;
      vozidlo::inicializace(nova_kola, nova_vaha);
    }
    void nakladni::inic_nakl(int kolik, float max_naklad){
      osob = kolik;
      naklad = max_naklad;
    }
    float nakladni::vykonost(void){
      return naklad / (naklad + ziskej_vahu());
    }
    int main(int argc, char **argv)
    {
      vozidlo unicykl;
      //   unicykl.inicializace(1, 7.5);
      cout << "Unicykl mß " << unicykl.ziskej_kola() << " kol.\n";
      cout << "Zßt∞╛ na kolo unicyklu je "<< unicykl.vaha_na_kolo() << endl;
      cout << "Vßha unicyklu je " << unicykl.ziskej_vahu() << " kg.\n";
      automobil sedan;
      //   sedan.inicializace(4, 1500.0, 5);
      cout << "Sedan mß " << sedan.pasazeru() << " osob.\n";
      cout << "Sedan mß zßt∞╛ " << sedan.vaha_na_kolo() << " kg na kolo.\n";
      cout << "Sedan vß╛φ " << sedan.ziskej_vahu() << " kg.\n";
      nakladni tatra;
      //   tatra.inicializace(2, 7500.0);
      //   tatra.inic_nakl(1, 7000.0);
      cout << "Vßha tatry je " << tatra.ziskej_vahu() << " kg.\n";
      cout << "V²konnost tatry je "<<100.0*tatra.vykonost()<<" procent.\n";
      return 0;
    }
    K zji╣t∞nφ, kdy je kter² konstruktor automaticky volßn p°idejte do v╣ech konstruktor∙ v tomto programu p°φkazy k v²pisu zprßvy informujφcφ o volßnφ konstruktoru. Prozkoumejte po°adφ volßnφ konstruktor∙ (i u odvozen²ch t°φd).
    Pokud neurΦφme n∞co jinΘho, vyvolß C++ nejprve bezparametrick² konstruktor rodiΦovskΘ t°φdy a potΘ zaΦne provßd∞t vlastnφ konstruktor. Pokud chceme inicializovat zd∞d∞nΘ slo╛ky jin²m zp∙sobem, musφme v hlaviΦce konstruktoru uvΘst za dvojteΦkou odpovφdajφcφ volßnφ rodiΦovskΘho konstruktoru.
  5. Ve skuteΦn²ch aplikacφch obvykle umis╗ujeme deklaraci a definici t°φdy do jednoho hlaviΦkovΘho a jednoho zdrojovΘho souboru. Tyto soubory majφ jmΘno, kterΘ odpovφdß jmΘnu t°φdy. Nap°. pokud mßme t°φdu mojeTrida, pak umφstφme jejφ definici do zdrojovΘho souboru MOJETRIDA.CPP a jejφ deklaraci do hlaviΦkovΘho souboru MOJETRIDA.H (m∙╛eme pou╛φvat dlouhß jmΘna soubor∙).

  6. Ukß╛eme si to na nßsledujφcφ konzolovΘ aplikaci. ZaΦneme v²voj novΘ konzolovΘ aplikace (ulo╛φme ji do souboru LETISTE.CPP). Obsah tohoto souboru je tento:
    #include <condefs.h>
    #include <iostream.h>
    #include <conio.h>
    #pragma hdrstop
    USEUNIT("letadlo.cpp");
    #include "letadlo.h"
    int ziskejVstup(int max);
    void ziskejPrvky(int& rychlost, int& smer, int& vyska);
    int main(int argc, char **argv)
    {
      char vracenaZprava[100];
      // nastavenφ pole letadel a vytvo°enφ t°φ objekt∙ letadel
      Letadlo* letadla[3];
      letadla[0] = new Letadlo("TWA 1040");
      letadla[1] = new Letadlo("United Express 749", DOPRAVNI);
      letadla[2] = new Letadlo("Cessna 3238T", SOUKROME);
      // zaΦßtek cyklu
      do {
        int letadlo, zprava, rychlost, vyska, smer;
        rychlost = vyska = smer = -1;
        cout << endl << "KterΘmu letadlu chcete zaslat zprßvu?";
        cout << endl << endl << "0. Konec" << endl;
        for (int i = 0; i < 3; i++)
          cout << (i + 1) << ". " << letadla[i]->jmeno << endl;
        // Zφskßnφ Φφsla letadla
        letadlo = ziskejVstup(4);
        // P°i 0 konec cyklu
        if (letadlo == -1) break;
        cout << endl<<"Vybrßno "<<letadla[letadlo]->jmeno<<endl<<endl;
        cout << "Jakou zprßvu chcete zaslat?" << endl;
        cout << endl << "0. Konec" << endl;
        cout << "1. Zm∞na stavu" << endl;
        cout << "2. Vzlet" << endl;
        cout << "3. P°istßnφ" << endl;
        cout << "4. Zprßva o stavu" << endl;
        zprava = ziskejVstup(5);
        if (zprava == -1) break;
        if (zprava == 0) ziskejPrvky(rychlost, smer, vyska);
        bool dobraZprava = letadla[letadlo]-> PrijmiZpravu(
          zprava, vracenaZprava, rychlost, smer, vyska);
        if (!dobraZprava) cout << endl << "Nelze provΘst.";
        cout << endl << vracenaZprava << endl;
      } while (1);
      for (int i = 0; i < 3; i++) delete letadla[i];
      return 0;
    }
    int ziskejVstup(int max)
    {
      int volba;
      do {
        volba = getch();
        volba -= 49;
      } while (volba < -1 || volba > max);
      return volba;
    }
    void ziskejPrvky(int& rychlost, int& smer, int& vyska)
    {
      cout << endl << "Zadej novou rychlost: ";
      cin >> rychlost;
      cout << "Zadej nov² sm∞r: ";
      cin >> smer;
      cout << "Zadej novou v²╣ku: ";
      cin >> vyska;
      cout << endl;
    }
    Dßle zvolφme File | New | Text a vytvo°φme hlaviΦkov² soubor t°φdy LETADLO.H (ulo╛φme jej do stejnΘho adresß°e jako LETISTE.CPP). Soubor bude mφt tento obsah:
    #ifndef letadloH
    #define letadloH
    #define LINKOVE     0
    #define DOPRAVNI    1
    #define SOUKROME    2
    #define VZLET       0
    #define LET         1
    #define PRISTANI    2
    #define NADRAZE     3
    #define ZPR_ZMENA   0
    #define ZPR_VZLET   1
    #define ZPR_PRIST   2
    #define ZPR_ZPRAVA  3
    class Letadlo {
      public:
        Letadlo(const char* _jmeno, int _typ = LINKOVE);
        ~Letadlo();
        virtual int ziskejStav(char* stavovyRetezec);
        int ziskejStav() {return stav; }
        int Rychlost() { return rychlost; }
        int Smer() { return smer; }
        int Vyska() { return vyska; }
        void ZpravaStav();
        bool PrijmiZpravu(int zpr, char* odpoved, int rych = -1,
                         int sme = -1, int vys = -1);
        char* jmeno;
      protected:
        virtual void Vzletni(int smer);
        virtual void Pristan();
      private:
        int rychlost;
        int vyska;
        int smer;
        int stav;
        int typ;
        int maxVyska;
    };
    #endif
    Obdobn∞ vytvo°φme dal╣φ soubor LETADLO.CPP (ulo╛φme jej op∞t do stejnΘho adresß°e) s obsahem:
    #include <stdio.h>
    #include <iostream.h>
    #include "letadlo.h"
    Letadlo::Letadlo(const char* _jmeno, int _typ):
      typ(_typ), stav(NADRAZE), rychlost(0), vyska(0), smer(0)
    {
      switch (typ) {
        case LINKOVE : maxVyska = 10000; break;
        case DOPRAVNI : maxVyska = 6000; break;
        case SOUKROME : maxVyska = 3000; break;
      }
      jmeno = new char[50];
      strcpy(jmeno, _jmeno);
    }
    Letadlo::~Letadlo()
    {
      delete[] jmeno;
    }
    bool Letadlo::PrijmiZpravu(int zpr, char* odpoved, int rych,
                         int sme, int vys)
    {
      if (rych > 800) {
        strcpy(odpoved, "Rychlost nem∙╛e b²t v∞t╣φ ne╛ 800.");
        return false;
      }
      if (sme > 360) {
        strcpy(odpoved, "Sm∞r nem∙╛e b²t v∞t╣φ ne╛ 360 stupn∙.");
        return false;
      }
      if (vys < 100 && vys != -1) {
        strcpy(odpoved, "Jsme p°φli╣ nφzko.");
        return false;
      }
      if (vys > maxVyska) {
        strcpy(odpoved, "To je p°φli╣ vysoko.");
        return false;
      }
      switch (zpr) {
        case ZPR_VZLET : {
          if (stav != NADRAZE) {
            strcpy(odpoved, "Jsem ji╛ ve vzduchu.");
            return false;
          }
          Vzletni(sme);
          break;
        }
        case ZPR_ZMENA : {
          if (stav == NADRAZE) {
            strcpy(odpoved, "Jsem na zemi.");
            return false;
          }
          if (rych != -1) rychlost = rych;
          if (sme != -1) smer = sme;
          if (vys != -1) vyska = vys;
          stav = LET;
          break;
        }
        case ZPR_PRIST : {
          if (stav == NADRAZE) {
            strcpy(odpoved, "Jsme na zemi.");
            return false;
          }
          Pristan();
          break;
        }
        case ZPR_ZPRAVA : ZpravaStav();
      }
      strcpy(odpoved, "Provedeno.");
      return true;
    }
    void Letadlo::Vzletni(int sme)
    {
      smer = sme;
      stav = VZLET;
    }
    void Letadlo::Pristan()
    {
      rychlost = smer = vyska = 0;
      stav = NADRAZE;
    }
    int Letadlo:: ziskejStav(char* stavovyRetezec)
    {
      sprintf(stavovyRetezec, "%s, V²╣ka: %d, Sm∞r: %d, Rychlost: %d\n",
              jmeno, vyska, smer, rychlost);
      return stav;
    }
    void Letadlo::ZpravaStav()
    {
      char buff[100];
      ziskejStav(buff);
      cout << endl << buff << endl;
    }
    Kdy╛ se podφvßme na hlaviΦkov² soubor t°φdy Letadlo, pak na jeho zaΦßtku vidφme °adu direktiv #define. Definujeme zde makra, kterß nahradφ textovΘ °et∞zce Φφseln²mi hodnotami (°et∞zce si zapamatujeme snadn∞ji ne╛ Φφsla). Posu∩te sami, kter² z nßsledujφcφch p°φkaz∙ je srozumiteln∞j╣φ?
    if (typ == LINKOVY) ...
    // nebo
    if (typ == 0) ...
    JmΘna t∞chto konstant obvykle zapisujeme velk²mi pφsmeny (pro snadnΘ odli╣enφ od prom∞nn²ch). Jin²m zp∙sobem deklarace konstant je deklarace prom∞nnΘ s modifikßtorem const. Nap°.
    const int LINKOVY = 0;
    Pou╛itφ konstantnφ prom∞nnΘ je modern∞j╣φ metoda ne╛ definice konstant pomocφ maker.
    Dal╣φ °ßdky hlaviΦkovΘho souboru obsahujφ deklaraci t°φdy. KrßtkΘ metody jsou zde deklarovßny jako vlo╛enΘ funkce. Je zde takΘ p°ekrytß funkce ziskejStav. Pov╣imn∞te si, ╛e ve t°φd∞ je pouze jedna ve°ejnß datovß slo╛ka. Ostatnφ jsou soukromΘ a jsou tedy dostupnΘ pouze pomocφ metod. Tzn. pokud po╛adujeme zm∞nu rychlosti, v²╣ky nebo sm∞ru, pak musφme instanci Letadlo zaslat zprßvu. To odpovφdß skuteΦnosti. ╪φdφcφ letovΘho provozu takΘ nem∙╛e fyzicky zm∞nit sm∞r letadla. M∙╛e pouze zaslat zprßvu pilotovi a ten po╛adovanou zm∞nu provede.
    Nynφ p°ejdeme k definiΦnφmu souboru t°φdy. Konstruktor provßdφ inicializaci, vΦetn∞ dynamickΘ alokace mφsta pro pole znak∙ k ulo╛enφ jmΘna letadla. Tato pam∞╗ je uvol≥ovßna v destruktoru. V∞t╣inu prßce provßdφ metoda PrijmiZpravu. P°φkaz switch urΦuje, kterß zprßva byla p°ijata a je provedena p°φslu╣nß akce. Pov╣imn∞te si, ╛e metody Vzletni a Pristan nemohou b²t volßny p°φmo (jsou chrßn∞nΘ), ale prost°ednictvφm PrijmiZpravu. Nelze tedy °φci letadlu aby vzlΘtlo nebo p°istßlo, ale lze mu zaslat zprßvu, aby to provedlo. Metoda ZpravaStav volß ZiskejStav k zφskßnφ stavovΘho °et∞zce, kter² potom metoda vypφ╣e.
    Hlavnφ program deklaruje pole ukazatel∙ na Letadlo a vytvß°φ t°i instance tΘto t°φdy. Dßle zaΦφnß cyklus ve kterΘm zasφlßme zprßvy objekt∙m Letadlo volßnφm funkce PrijmiZpravu. Po odeslßnφ zprßvy, Φekßme na odpov∞∩ od letadla. Tento cyklus pob∞╛φ stßle, dokud nenφ ukonΦen p°φkazem break.
    Rozd∞lovat program do n∞kolika soubor∙ je vhodnΘ u slo╛it∞j╣φch aplikacφ. U na╣ich (zatφm jednoduch²ch) aplikacφ z∙staneme u jednoho zdrojovΘho souboru.

Kontrolnφ otßzky:

  1. Jak m∙╛eme dosßhnou toho, ╛e metody budou nedotupnΘ z vn∞j╣ku t°φdy a budeme je moci volat v odvozen²ch t°φdßch?
  2. Co je to objekt?
  3. M∙╛e mφt t°φda vφce ne╛ jeden konstruktor?
  4. Jak se li╣φ t°φda od struktury v C++?
  5. Jak² v²znam majφ soukromΘ datovΘ slo╛ky?
  6. Jak m∙╛eme u soukrom²ch datov²ch slo╛ek umo╛mit u╛ivateli Φφst a nastavovat jejich hodnoty?
  7. Jak a kdy je volßn destruktor t°φdy?
  8. K Φemu slou╛φ inicializaΦnφ seznam t°φdy?
  9. M∙╛e t°φda obsahovat instance jinΘ t°φdy jako datovΘ slo╛ky?
╪e╣enφ


4. Zßklady OOP IV