Historia pytania:
Zakres zmiennych to fundamentalna koncepcja, która istnieje od początku języków programowania. W C++ zasady zakresu zmiennych znacznie się rozwijały wraz z pojawieniem się nowych standardów (od lokalnych deklaracji po anonimowe przestrzenie nazw, wyrażenia lambda i bloki try/catch).
Problem:
Nieprawidłowe zrozumienie zakresu zmiennych prowadzi do błędów w czasie kompilacji lub wykonania, takich jak kolizje nazw, przypadkowe ukrywanie zmiennych, wycieki pamięci oraz niezinicjalizowane wartości.
Rozwiązanie:
W C++ podstawowymi poziomami zakresu zmiennych są:
Ważne jest, aby pamiętać o zasadach wyszukiwania identyfikatorów (name lookup): kompilator szuka najbliższej definicji, a następnie porusza się w górę.
Przykład kodu:
#include <iostream> void func() { int x = 1; { int x = 2; // ukrycie zewnętrznej zmiennej x std::cout << x << '\n'; // Wyświetli: 2 } std::cout << x << '\n'; // Wyświetli: 1 } int x = 10; // zmienna globalna int main() { func(); std::cout << x << '\n'; // Wyświetli: 10 }
Kluczowe cechy:
Czy zmienna zadeklarowana w pliku nagłówkowym poza jakąkolwiek funkcją może powodować błąd link-time?
Tak! Jeśli zmienna jest zadeklarowana jako po prostu int value (bez extern i bez inicjalizacji inline w C++17), stworzy to wiele definicji i doprowadzi do błędu wielokrotnej definicji na etapie linkowania.
Przykład kodu:
// myheader.h int globalVar = 5; // ZŁE: definicja, a nie deklaracja
Co się stanie, jeśli zadeklarujesz zmienną o tej samej nazwie wewnątrz bloku wewnętrznego?
Wewnętrzna zmienna „ukryje” zewnętrzną, a wszystkie odwołania będą kierowane do niej, dopóki nie zakończy się wewnętrzny blok.
Czy zmienna zadeklarowana w nagłówku funkcji jest dostępna w innych funkcjach?
Nie. Zmienne zadeklarowane (i zdefiniowane) wewnątrz ciała funkcji istnieją tylko podczas wykonywania tej funkcji. Są niedostępne poza nią.
W projekcie do przechowywania stanu używana jest zmienna globalna, zdefiniowana od razu w kilku plikach źródłowych poprzez inkluzję nagłówka.
Zalety: Łatwo się do niej odwołać z dowolnego miejsca.
Wady: Trudności w debugowaniu, wielokrotna definicja (błąd linker'a), brak bezpieczeństwa wątkowego, nieoczekiwane wartości.
Użycie lokalnych zmiennych, przekazywanie stanu przez parametry funkcji, prawie brak zmiennych globalnych lub używanie extern tylko w połączeniu z inkapsulacją przez przestrzenie nazw.
Zalety: Przejrzystość kodu, zarządzanie zależnościami, łatwość testowania.
Wady: Czasami wymaga więcej kodu niż z zmiennymi globalnymi.