IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

9. diel - Statika v C ++

V predchádzajúcom cvičení, Riešené úlohy k 6. až 8. lekciu OOP v C ++, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 6. až 8. lekciu OOP v C ++ , sme si popísali konštantný metódy v C ++. V dnešnom C ++ tutoriálu sa budeme venovať pojmu statika. Až doteraz sme boli zvyknutí, že dáta (stav) nesie inštancie. Atribúty, ktoré sme definovali, teda patrili inštanciu a boli pre každú inštanciu jedinečné (každý bojovník mal svoje životy). OOP však umožňuje definovať atribúty a metódy na samotnej triede. Týmto prvkom hovoríme statické (niekedy triednej) a sú spoločné pre všetky inštancie.

POZOR! Dnešné lekcie vám ukáže statiku, teda postupy, ktoré v podstate narušujú objektový model. OOP je obsahuje len pre špeciálne prípady a všeobecne platí, že všetko ide napísať bez statiky. Vždy musíme starostlivo zvážiť, či statiku naozaj nutne potrebujeme. Všeobecne by som odporúčal statiku vôbec nepoužívať, ak si nie ste úplne istí, čo robíte. Podobne ako globálne premenné, je statika v objektovom programovaní niečo, čo umožňuje písať zlý kód a porušovať dobré praktiky. Dnes si ju skôr vysvetlíme, aby ste pochopili určité metódy a triedy, ktoré ju používajú. Znalosti použite s rozvahou, na svete bude potom menej zla.

Statické (triedny) atribúty

Ako statické môžeme označiť rôzne prvky. Začnime u atribútov. Ako som sa už v úvode spomenul, statické prvky patrí triede, nie inštanciu. Dáta v nich uložené teda môžeme čítať bez ohľadu na to, či nejaká inštancia existuje. V podstate môžeme povedať, že statické atribúty sú spoločné pre všetky inštancie triedy, ale nie je to presné, pretože s inštanciami naozaj vôbec nesúvisí. Napríklad by sme vyžadovali, aby dĺžka mená boli aspoň 4 znaky. Táto konštanta by nemala byť vložená priamo v kóde, ale mali by sme si ju niekde uchovať (ak by sme ju chceli neskôr zmeniť). Uchováme ju práve ako statický atribút. Syntax je rovnaká ako u atribútu, iba pred neho pridáme kľúčové slovo static.

Hrac.h

class Hrac
{
private:
    Bojovnik bojovnik;
    string jmeno;
public:
    static int MIN_DELKA_JMENA;
    Hrac(string jmeno, Kostka &kostka);
    string getJmeno();
    Bojovnik& getBojovnik();
};

Hrac.cpp

int Hrac::MIN_DELKA_JMENA = 4;

Hrac::Hrac(string jmeno, Kostka &kostka) : bojovnik(100, 8, 5, kostka)
{
    if (jmeno.length() < MIN_DELKA_JMENA)
        exit(1);
    this->jmeno = jmeno;
}

Všimnite si rozdelenie deklarácie a inicializácia. Deklarácia je v súbore Hrac.h a obsahuje kľúčové slovo static. Inicializácia musí byť v .cpp súboru a už slovo static nepoužíva. Teraz máme statický atribút, ktorý ale ide meniť. Pretože sa jedná o konštantu, mali by sme ju ako konštantu označiť. Pre konštanty musí byť deklarácia a inicializácia v .h súboru (časť v Hrac.cpp zmažeme).

Hrac.h

class Hrac
{
private:
    Bojovnik bojovnik;
    string jmeno;
public:
    static const int MIN_DELKA_JMENA = 4;
    Hrac(string jmeno, Kostka &kostka);
    string getJmeno();
    Bojovnik& getBojovnik();
};

Pretože sa často jedná o zdroj chýb, rýchle zopakovanie: konštantný statické atribúty musí byť inicializované v .h súboroch, zatiaľ čo iba statické atribúty musí byť inicializované v .cpp súboroch.

Samotné meno statického atribútu (rovnako ako u klasických atribútoch) môžeme použiť iba v metódach triedy (MIN_DELKA_JMENA). Ak by sme konštantu chceli použiť napríklad vo funkcii main(), musíme ju špecifikovať is názvom triedy (Hrac::MIN_DELKA_JMENA).

Pozn .: Rovnaký zápis by sme mohli použiť aj vo vnútri triedy Hrac, záleží na programátorovi.

cout << "Min delka jmena: " << Hrac::MIN_DELKA_JMENA << endl;

Statickej metódy

Statické metódy sa volajú na triede. Ide najmä o pomocné metódy, ktoré potrebujeme často používať a neoplatí sa nám tvoriť inštanciu. Ukážme si opäť reálny príklad. Pri vytváraní arény sa v konstruktoru pýtame na mená. To je zlý prístup - konštruktor by mal slúžiť len a len k vytvoreniu inštancie. Akákoľvek ďalšia logika (ako napríklad získavanie vstupov) je nežiaduce. Musím na to upozorniť, pretože som sa niekoľkokrát stretol s porušením tohto pravidla. Napríklad konštruktor spúšťal samotný algoritmus, ktorý trieda reprezentovala. To je z hľadiska OOP aj všeobecných praktík úplne zle. Konštruktor má iba vytvoriť inštanciu, nič viac. Ako opravíme našu Arénu? Najskôr zmeníme konštruktor tak, aby iba prijímal polia mien a ich počet. A samozrejme dodáme statickú metódu, ktorá sa spýta na mená a arénu pre nás vytvorí.

Najskôr upravíme hlavičkový súbor Arena.h - musíme zmeniť parametre konstruktoru a pridať deklaráciu statické metódy.

Arena.h

#ifndef __ARENA_H_
#define __ARENA_H_
#include "Hrac.h"
#include "Kostka.h"

class Arena
{
private:
    Hrac** hraci;
    int pocet_hracu;
    int tah;
    bool existujeVitaz();
    int pocetZivych();
public:
    Arena(string* jmena, int pocet_hracu, Kostka &kostka); // upraveno
    ~Arena();
    void vypis();
    void vypisViteze();
    void zapas();
    static Arena* vytvorArenu(int pocet_hracu, Kostka &kostka); // statická metoda
};
#endif

Teraz musíme zmeniť implementáciu konstruktoru a naimplementovať našu statickú metódu:

Arena.cpp

// ...další implementace
Arena::Arena(string* jmena, int pocet_hracu, Kostka &kostka) : tah(1)
{
    this->pocet_hracu = pocet_hracu;
    this->hraci = new Hrac*[pocet_hracu];
    for (int i = 0; i < pocet_hracu; i++)
        this->hraci[i] = new Hrac(jmena[i], kostka);

}

Arena* Arena::vytvorArenu(int pocet_hracu, Kostka &kostka)
{
    string* jmena = new string[pocet_hracu]; // vytvoříme si pole pro jména
    for (int i = 0; i < pocet_hracu; i++)
    {
        cout << "Zadejte jmeno hrace: ";
        cin >> jmena[i];
    }
    Arena* arena = new Arena(jmena, pocet_hracu, kostka);
    delete[] jmena; // toto pole nesmíme zapomenout smazat
    return arena;
}

A to je všetko :) . Zostáva už len upraviť súbor main.cpp, aby používal našu novú statickú metódu:

Main.cpp

#include <iostream>
#include "Kostka.h"
#include "Arena.h"
using namespace std;


int main()
{
    Kostka kostka;
    Arena* arena = Arena::vytvorArenu(3, kostka); // volání statické metody
    arena->zapas();
    arena->vypis();
    arena->vypisViteze();
    cin.get(); cin.get();
    return 0;
}

Rovnako ako u statického atribútu, pristupujeme k metóde pomocou názvu triedy, dvoch dvojbodiek a názvu metódy.

Oprava kocky

Teraz, keď už vieme pracovať so statickými atribúty, môžeme opraviť našu triedu Kostka. Že nemáme čo opravovať? Skúste si spustiť nasledujúci kód:

int main()
{
    Kostka kostka1;
    for (int i = 0; i < 10; i++)
        cout << kostka1.hod() << " ";
    cout << endl;
    Kostka kostka2;
    for (int i = 0; i < 10; i++)
        cout << kostka2.hod() << " ";
    cout << endl;
    return 0;
}
#ifndef __KOSTKA_H__
#define __KOSTKA_H__
using namespace std;

class Kostka
{
private:
    int pocet_sten;
public:
    Kostka();
    Kostka(int pocet_sten);
    int hod();
    int getPocetSten();
};
#endif
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Kostka.h"

using namespace std;
Kostka::Kostka() : Kostka(6)
{
}

Kostka::Kostka(int pocet_sten)
{
    this->pocet_sten = pocet_sten;
    srand((unsigned int)time(NULL));
}

int Kostka::hod()
{
    return rand() % this->pocet_sten + 1;
}

int Kostka::getPocetSten()
{
    return this->pocet_sten;
}

Uvidíte, že obe kocky nahádzali rovnakej hodnoty. Ako je to možné? V konstruktoru kocky sme zmenili počiatočný bod generátora (viď. Lekcie o konštruktor v C ++). Ale hodnota funkcie time(), ktorú používame pre inicializáciu, sa mení iba raz za sekundu. Pri vytvorení druhej kocky sa teda znovu zavolal konštruktor a znovu sa nastavil počiatočný bod generátora na rovnakú hodnotu, ako to bolo pri prvej kocky. Ako z toho von? Pre celú aplikáciu nám stačí, keď inicializácia prebehne iba raz. Vytvoríme si teda (privátne) statický atribút, ktorý nám bude hovoriť, či sme inicializácii vykonali. Na základe tejto premennej budeme inicializovať iba raz.

// Kostka.h
class Kostka
{
private:
    int pocet_sten;
    static bool inicializovano;
public:
    Kostka();
    Kostka(int pocet_sten);
    int hod();
    int getPocetSten();
};
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Kostka.h"
bool Kostka::inicializovano = false;

Kostka::Kostka(int pocet_sten)
{
    this->pocet_sten = pocet_sten;
    if (!Kostka::inicializovano)  // nebo if(!inicializovano)
    {
        srand((unsigned int)time(NULL));
        inicializovano = true; // nebo Kostka::inicializovano = true;
    }
}

Kostka::Kostka() : Kostka(6)
{
}

int Kostka::hod()
{
    return rand() % this->pocet_sten + 1;
}

int Kostka::getPocetSten()
{
    return this->pocet_sten;
}
#include <iostream>
#include "Kostka.h"
using namespace std;
int main()
{
    Kostka kostka1;
    for (int i = 0; i < 10; i++)
        cout << kostka1.hod() << " ";
    cout << endl;
    Kostka kostka2;
    for (int i = 0; i < 10; i++)
        cout << kostka2.hod() << " ";
    cout << endl;
    return 0;
}

Teraz už program funguje správne. Tým máme statiku ukončenú a nabudúce, Riešené úlohy k 9. lekcii OOP v C ++ , sa pozrieme na preťažovanie operátorov, takže sa máte na čo tešiť.

V nasledujúcom cvičení, Riešené úlohy k 9. lekcii OOP v C ++, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 56x (9.76 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++

 

Predchádzajúci článok
Riešené úlohy k 6. až 8. lekciu OOP v C ++
Všetky články v sekcii
Objektovo orientované programovanie v C ++
Preskočiť článok
(neodporúčame)
Riešené úlohy k 9. lekcii OOP v C ++
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity