{{page>courses:b6b36pjc:styles#common&noheader&nofooter}} {{page>courses:b6b36pjc:styles#cviceni&noheader&nofooter}} ===== Cvičení 12: 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:b6b36pjc: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:b6b36pjc: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:b6b36pjc: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: const int pocetDruhuOvoce = 130; struct Zelenina { static const int pocetDruhuZeleniny = 131; ... }; 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''). 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; ... }; 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: // header.hpp struct Zelenina { static std::vector jidlo; static const int pocetDruhuNaZacatku; } // lilek.cpp #include "header.hpp" std::vector Zelenina::jidlo(1, "mrkev"); // celer.cpp #include "header.hpp" const int Zelenina::pocetDruhuNaZacatku = Zelenina::jidlo.size(); 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í. // header.hpp struct Zelenina { static std::vector& jidlo(); static const int pocetDruhuNaZacatku; } // lilek.cpp #include "header.hpp" std::vector& Zelenina::jidlo() { static std::vector 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(); * 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:b6b36pjc: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()''.