Search
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.
jidlo
celer()
lilek()
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.
extern
g_jidlo
Řešení
pocitadlo()
static
Zelenina
lilek
celer
Zelenina::lilek()
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á.
jídlo
LisNaJesterkyAHady
LisNaJesterkyAHady::pocetKonstrukci()
LisNaJesterkyAHady::pocetDestrukci()
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
const long
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).
constexpr
const
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”.
Š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<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();
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.
pocetDruhuNaZacatku
1
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<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();
Proměnná označená static nám pomůže i správně implementovat návrhový vzor singleton (jedináček).
Jedinacek
instance()