Warning
This page is located in archive.

Differences

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

Link to this comparison view

courses:b6b36pjc:cviceni:cviceni_12 [2017/09/25 17:17] (current)
richta created
Line 1: Line 1:
 +<​HTML>​
 +<​style>​
 +.code .kw1, .code .kw2, .code .kw4 { color: #00f; font-weight:​ bold; }
 +.code .br0, .code .kw3, .code .me1, .code .me2, .code .nu0 { color: #000; }
 +.code .co1, .code .coMULTI { color: #080; font-style: normal; }
 +.code .co2 { color: #888; font-style: normal; }
 +.code .st0 { color: #a31515; }
 +</​style>​
 +</​HTML>​
 +
 +===== Globální proměnné =====
 +
 +Globální proměnné vyžadují podobné zacházení jako funkce -- existují jejich definice a deklarace, a definice smí být jenom jedna. Platí, že v celém programu může být jenom jedna globální proměnná s daným názvem a typem.
 +
 +  * Stáhněte si {{:​courses:​a7b36pjc:​cviceni:​cviceni_12_init.zip|výchozí kód}}.
 +  * Udělejte z proměnné ''​jidlo''​ globální proměnnou.
 +  * Přidejte do hlavičky deklaraci proměnné ''​jidlo''​.
 +  * Přidejte ve funkcích ''​celer()''​ a ''​lilek()''​ nové položky do vektoru ''​jidlo''​.
 +
 +Abychom napsali deklaraci globální proměnné, museli jsme na začátek přidat klíčové slovo ''​extern''​. Také je žádoucí si globální proměnnou nějak označit, třeba ji přejmenovat na ''​g_jidlo''​.
 +
 +{{:​courses:​a7b36pjc:​cviceni:​cviceni_12_global.zip|Řešení}}
 +
 +==== Globální proměnné ve funkcích ====
 +
 +  * Vytvořte funkci ''​pocitadlo()'',​ která vypíše, pokolikáté jsme ji zavolali. Udržuje si počet volání v globální proměnné.
 +  * Přesuňte globální proměnnou dovnitř funkce. Použijte klíčové slovo ''​static''​.
 +
 +==== Globální proměnné ve třídách ====
 +
 +  * Vytvořte třídu (nebo strukturu) ''​Zelenina''​.
 +  * Přesuňte ​ funkce ''​lilek''​ a ''​celer''​ do třídy ''​Zelenina''​ a označte tyto funkce jako statické. Statické funkce nevyžadují přítomnost instance třídy a lze je zavolat přímo pomocí syntaxe ''​Zelenina::​lilek()''​.
 +  * Přesuňte globální proměnnou ''​jidlo''​ do třídy ''​Zelenina'',​ použijte klíčové slovo ''​static''​. Statická data také nevyžadují přítomnost instance třídy, jsou to vlastně globální proměnné. Odkážeme se na ně pomocí syntaxe ''​Zelenina::​jidlo''​.
 +
 +Stejně jako předtím, i teď musíme proměnnou ''​jídlo''​ definovat, a to v právě jedné kompilační jednotce. Všimněte si, že to je stejné, jako když ''​jidlo''​ byla obyčejná globální proměnná.
 +
 +  * Vytvořte třídu ''​LisNaJesterkyAHady'',​ která počítá, kolikrát byla zkonstruována a kolikrát byla zdestruována. Počet konstrukcí a destrukcí uchovávejte ve statických proměnných třídy. Statické proměnné zapouzdřete a přistupujte k nim pomocí statických metod ''​LisNaJesterkyAHady::​pocetKonstrukci()''​ a ''​LisNaJesterkyAHady::​pocetDestrukci()''​.
 +
 +{{:​courses:​a7b36pjc:​cviceni:​cviceni_12_lis.zip|Řešení}}
 +
 +===== Konstanty jednoduchých typů =====
 +
 +Pro celočíselné konstanty (''​const int'',​ ''​const long'',​ všechny konstantní enumy, apod.) platí výjimka z pravidla jedné definice -- jako globální proměnné mohou být definovány opakovaně. Díky tomu můžeme do hlavičky umístit tento kód:
 +
 +<code cpp>
 +const int pocetDruhuOvoce = 130;
 +
 +struct Zelenina {
 +    static const int pocetDruhuZeleniny = 131;
 +    ...
 +};
 +</​code>​
 +
 +Některé další jednoduché typy lze také použít jako konstanty, pokud máme C%%++%%11. Někdy je ale nutné použít ''​constexpr''​ místo ''​const''​ (pokud nad tím nechcete přemýšlet,​ dá se psát všude ''​constexpr''​).
 +
 +<code cpp>
 +const float pi = 3.14;
 +const int pocetDruhuOvoce = 130;
 +
 +struct Zelenina {
 +    static constexpr float pi = 3.14f; // tady musí být constexpr
 +    static const int pocetDruhuZeleniny = 131;
 +    ...
 +};
 +</​code>​
 +
 +Klíčové slovo ''​constexpr''​ obecně znamená něco jako "je to konstanta známá během kompilace",​ zatímco ''​const''​ se blíží více významu "dá se nastavit jenom jednou a pak se nesmí měnit"​.
 +
 +===== Problém s pořadím inicializace =====
 +
 +Špatná situace nastává ve chvíli, když potřebujete jednu globální proměnnou inicializovat na základě hodnoty v jiné globální proměnné. V rámci jedné kompilační jednotky se ještě všechno chová rozumně, globální proměnné jsou inicializovány v tom pořadí, v jakém jsou inicializace napsány. Problém nastane ve chvíli, když máme inicializace v různých kompilačních jednotkách:​
 +
 +<code cpp>
 +// header.hpp
 +struct Zelenina {
 +    static std::​vector<​std::​string>​ jidlo;
 +    static const int pocetDruhuNaZacatku;​
 +}
 +
 +// lilek.cpp
 +#include "​header.hpp"​
 +std::​vector<​std::​string>​ Zelenina::​jidlo(1,​ "​mrkev"​);​
 +
 +// celer.cpp
 +#include "​header.hpp"​
 +const int Zelenina::​pocetDruhuNaZacatku = Zelenina::​jidlo.size();​
 +</​code>​
 +
 +Potíž je v tom, že pořadí inicializace globálních proměnných v různých kompilačních jednotkách není definováno. Nelze říct, jestli nejdřív proběhne inicializace proměnné ''​jidlo''​ nebo konstanty ''​pocetDruhuNaZacatku''​. Takže hodnota v konstantě může být ''​1'',​ ale taky tam může být něco úplně jiného.
 +
 +Naštěstí existuje jednoduché řešení, a to je ukrýt globální proměnné do funkcí a metod. Globály ukryté ve funkcích se inicializují ve chvíli, kdy se poprvé provede řádek s jejich definicí.
 +
 +<code cpp>
 +// header.hpp
 +struct Zelenina {
 +    static std::​vector<​std::​string>&​ jidlo();
 +    static const int pocetDruhuNaZacatku;​
 +}
 +
 +// lilek.cpp
 +#include "​header.hpp"​
 +std::​vector<​std::​string>&​ Zelenina::​jidlo() {
 +    static std::​vector<​std::​string>​ val(1, "​mrkev"​);​ // inicializace val se provede, až program bude na tomto řádku
 +    return val;
 +}
 +
 +// celer.cpp
 +#include "​header.hpp"​
 +const int Zelenina::​pocetDruhuNaZacatku = Zelenina::​jidlo().size();​
 +</​code>​
 +
 +  * Ukryjte globální proměnnou ''​pocetDruhuNaZacatku''​ do statické metody, podobně jako jsme to udělali s globální proměnnou ''​jidlo''​. Co se stane, když ''​pocetDruhuNaZacatku''​ zavoláme poprvé až na konci programu?
 +
 +{{:​courses:​a7b36pjc:​cviceni:​cviceni_12_initorder.zip|Řešení}}
 +
 +===== Singleton =====
 +
 +Proměnná označená ''​static''​ nám pomůže i správně implementovat návrhový vzor singleton (jedináček).
 +
 +  * Napište třídu ''​Jedinacek''​ a přidejte do ní statickou metodu ''​instance()'',​ která poskytne jeho instanci.
 +  * Zakažte nebo skryjte klíčové operace třídy, abyste zajistili, že jediná instance je ta od metody ''​instance()''​.
 +
  
courses/b6b36pjc/cviceni/cviceni_12.txt · Last modified: 2017/09/25 17:17 by richta