W lekcji 7.3 -- Zmienne lokalne, omawialiśmy, że zmienne lokalne to zmienne zdefiniowane wewnątrz treści funkcji. Zmienne lokalne mają zasięg blokowy (są widoczne tylko w bloku, w którym są zadeklarowane) i mają automatyczny czas trwania (są tworzone w momencie definicji i niszczone po wyjściu z bloku).
W C++ zmienne można również zadeklarować poza funkcji. Takie zmienne nazywane są zmiennymi globalnymi.
Deklarowanie zmiennych globalnych
Zgodnie z konwencją zmienne globalne deklaruje się na górze pliku, poniżej dołączeń, w globalnej przestrzeni nazw. Oto przykład definiowania zmiennej globalnej:
#include <iostream>
// Variables declared outside of a function are global variables
int g_x {}; // global variable g_x
void doSomething()
{
// global variables can be seen and used everywhere in the file
g_x = 3;
std::cout << g_x << '\n';
}
int main()
{
doSomething();
std::cout << g_x << '\n';
// global variables can be seen and used everywhere in the file
g_x = 5;
std::cout << g_x << '\n';
return 0;
}
// g_x goes out of scope herePowyższy przykład wypisuje:
3 3 5
Zakres zmiennych globalnych
Identyfikatory zadeklarowane w globalnej przestrzeni nazw mają globalny zasięg przestrzeni nazw (powszechnie nazywany zasięgiem globalnym, a czasami nieformalnie zwane zakres pliku), co oznacza, że są widoczne od momentu deklaracji aż do końca pliku w którym są zadeklarowane.
Po zadeklarowaniu zmienna globalna może być używana w dowolnym miejscu pliku od tego momentu! W powyższym przykładzie zmienna globalna g_x jest używana w obu funkcjach doSomething() i main().
Zmienne globalne można również definiować w przestrzeni nazw zdefiniowanej przez użytkownika. Oto ten sam przykład co powyżej, ale g_x został przeniesiony z zakresu globalnego do przestrzeni nazw zdefiniowanej przez użytkownika Foo:
#include <iostream>
namespace Foo // Foo is defined in the global scope
{
int g_x {}; // g_x is now inside the Foo namespace, but is still a global variable
}
void doSomething()
{
// global variables can be seen and used everywhere in the file
Foo::g_x = 3;
std::cout << Foo::g_x << '\n';
}
int main()
{
doSomething();
std::cout << Foo::g_x << '\n';
// global variables can be seen and used everywhere in the file
Foo::g_x = 5;
std::cout << Foo::g_x << '\n';
return 0;
}Chociaż identyfikator g_x jest teraz ograniczony do zakresu namespace Foo, nazwa ta jest nadal dostępna globalnie (przez Foo::g_x) i g_x jest nadal zmienną globalną.
Kluczowa informacja
Zmienne zadeklarowane w przestrzeni nazw są również globalne zmienne.
Najlepsza praktyka
Wolę definiować zmienne globalne w przestrzeni nazw, a nie w globalnej przestrzeni nazw.
Zmienne globalne mają statyczny czas trwania
Zmienne globalne są tworzone podczas uruchamiania programu (przed main() rozpoczęciem wykonywania) i niszczone po jego zakończeniu. Nazywa się to czas statyczny z czas statyczny są czasami nazywane zmiennymi statycznymi.
Nazewnictwo zmiennych globalnych
Zgodnie z konwencją niektórzy programiści poprzedzają identyfikatory zmiennych globalnych przedrostkiem „g” lub „g_”, aby wskazać, że są one globalne. Przedrostek ten służy kilku celom:
- Pomaga uniknąć kolizji nazewnictwa z innymi identyfikatorami w pliku globalnym. przestrzeni nazw.
- Pomaga zapobiegać niezamierzonemu cieniowaniu nazw (omówimy ten punkt w dalszej części lekcji 7.5 — Ukrywanie zmiennych (ukrywanie nazw)).
- Pomaga wskazać, że zmienne z prefiksami pozostają poza zakresem funkcji, a zatem wszelkie zmiany, których w nich dokonamy, również zostaną zachowane.
Zmienne globalne zdefiniowane w przestrzeni nazw zdefiniowanej przez użytkownika często pomijają prefiks (ponieważ pierwsze dwa punkty na powyższej liście nie stanowią w tym przypadku problemu i możemy wywnioskować, że zmienna jest globalna, gdy zobaczymy dołączoną na początku nazwę przestrzeni nazw). Jednak nie zaszkodzi, jeśli chcesz zachować przedrostek jako bardziej widoczne przypomnienie trzeciego punktu.
Najlepsza praktyka
Rozważ użycie przedrostka „g” lub „g_” podczas nazywania zmiennych globalnych (szczególnie tych zdefiniowanych w globalnej przestrzeni nazw), aby pomóc odróżnić je od lokalnych zmiennych i parametrów funkcji.
Nota autora
Czasami otrzymujemy opinie od czytelników z pytaniami, czy przedrostki takie jak g_ są w porządku, ponieważ powiedziano im, że przedrostki są formą notacji węgierskiej oraz „notacja węgierska jest zła”.
Sprzeciw wobec notacji węgierskiej wynika głównie ze stosowania notacji węgierskiej do kodowania typ zmiennej w nazwie zmiennej, np. nAge, gdzie n oznacza int. Nie jest to zbyt przydatne we współczesnym C++.
Jednak używanie przedrostków (zwykle g/g_, s/s_, I m/m_) do reprezentowania zakres lub czasu trwania zmiennej dodaje wartość z tych powodów zaznaczono w tej sekcji.
Inicjalizacja zmiennej globalnej
W przeciwieństwie do zmiennych lokalnych, które są domyślnie niezainicjowane, zmienne o statycznym czasie trwania są domyślnie inicjowane od zera.
Niestałe zmienne globalne mogą być opcjonalnie inicjowane:
int g_x; // no explicit initializer (zero-initialized by default)
int g_y {}; // value initialized (resulting in zero-initialization)
int g_z { 1 }; // list initialized with specific valueStałe zmienne globalne
Podobnie jak zmienne lokalne, zmienne globalne mogą być stałe. Podobnie jak w przypadku wszystkich stałych, stałe zmienne globalne muszą zostać zainicjowane.
#include <iostream>
const int g_x; // error: constant variables must be initialized
constexpr int g_w; // error: constexpr variables must be initialized
const int g_y { 1 }; // const global variable g_y, initialized with a value
constexpr int g_z { 2 }; // constexpr global variable g_z, initialized with a value
void doSomething()
{
// global variables can be seen and used everywhere in the file
std::cout << g_y << '\n';
std::cout << g_z << '\n';
}
int main()
{
doSomething();
// global variables can be seen and used everywhere in the file
std::cout << g_y << '\n';
std::cout << g_z << '\n';
return 0;
}
// g_y and g_z goes out of scope herePowiązana treść
Stałe globalne omówimy bardziej szczegółowo na lekcji 7.10 — Współdzielenie stałych globalnych w wielu plikach (przy użyciu zmiennych wbudowanych).
Ostrzeżenie dotyczące (niestałych) zmiennych globalnych
Nowych programistów często kusi, aby używać wielu zmiennych globalnych, ponieważ można ich używać bez konieczności jawnego przekazywania ich do każdej funkcji, która ich potrzebuje. Jednakże ogólnie należy unikać stosowania niestałych zmiennych globalnych! Omówimy dlaczego w nadchodzącej lekcji 7.8 — Dlaczego (nie stałe) zmienne globalne są złe.
Szybkie podsumowanie
// Non-constant global variables
int g_x; // defines non-initialized global variable (zero initialized by default)
int g_x {}; // defines explicitly value-initialized global variable
int g_x { 1 }; // defines explicitly initialized global variable
// Const global variables
const int g_y; // error: const variables must be initialized
const int g_y { 2 }; // defines initialized global const
// Constexpr global variables
constexpr int g_y; // error: constexpr variables must be initialized
constexpr int g_y { 3 }; // defines initialized global constexpr
