7.5 — Cieniowanie zmiennych (ukrywanie nazw)

Każdy blok definiuje swój własny obszar zasięgu. Co więc się stanie, gdy w zagnieżdżonym bloku mamy zmienną, która ma taką samą nazwę jak zmienna w bloku zewnętrznym? Kiedy tak się dzieje, zagnieżdżona zmienna „ukrywa” zmienną zewnętrzną w obszarach, w których obydwie wchodzą w zakres. To się nazywa ukrywanie imienia lub cieniowanie.

Zacienianie zmiennych lokalnych

#include <iostream>

int main()
{ // outer block
    int apples { 5 }; // oto zewnętrzne jabłka bloku

    { // nested block
        // jabłka odnosi się tutaj do jabłek z zewnętrznego bloku
        std::cout << apples << '\n'; // wydrukuj wartość zewnętrznego bloku jabłka

        int apples{ 0 }; // zdefiniuj jabłka w zakresie zagnieżdżonego bloku

        // jabłka odnosi się teraz do zagnieżdżonych jabłek blokowych
        // zewnętrzny blok jabłek jest tymczasowo ukryty

        apples = 10; // to przypisuje wartość 10 do zagnieżdżonych jabłek blokowych, a nie zewnętrznych jabłek blokowych

        std::cout << apples << '\n'; // wydrukuj wartość zagnieżdżonego bloku jabłka
    } // zniszczenie zagnieżdżonych jabłek blokowych


    std::cout << apples << '\n'; // wypisuje wartość zewnętrznych bloków jabłek

    return 0;
} // zewnętrzne jabłka blokowe zniszczone

Jeśli uruchomisz ten program, wyświetli się:

5
10
5

W powyższym programie najpierw deklarujemy zmienną o nazwie apples w bloku zewnętrznym. Zmienna ta jest widoczna w obrębie wewnętrznego bloku, co możemy zobaczyć drukując jej wartość (5). Następnie deklarujemy inną zmienną (również o nazwie apples) w zagnieżdżonym bloku. Od tego miejsca do końca bloku nazwa apples odnosi się do zagnieżdżonego bloku apples, a nie zewnętrzny blok apples.

Zatem, kiedy przypisujemy wartość 10 Do apples, przypisujemy go do zagnieżdżonego bloku apples. Po wydrukowaniu tej wartości (10), zagnieżdżony blok kończy się i zagnieżdżony blok apples jest zniszczony. Istnienie i wartość bloku zewnętrznego apples nie ma to wpływu i udowadniamy to, drukując wartość zewnętrznego bloku apples (5).

Należy pamiętać, że jeśli zagnieżdżony blok apples nie została zdefiniowana, nazwa apples w zagnieżdżonym bloku nadal będzie odnosić się do bloku zewnętrznego apples, a więc przypisanie wartości 10 Do apples dotyczyłoby bloku zewnętrznego apples:

#include <iostream>

int main()
{ // outer block
    int apples{5}; // oto zewnętrzne jabłka bloku

    { // nested block
        // jabłka odnosi się tutaj do jabłek z zewnętrznego bloku
        std::cout << apples << '\n'; // wydrukuj wartość zewnętrznego bloku jabłka

        // brak zdefiniowanych jabłek bloków wewnętrznych w tym przykładzie

        apples = 10; // dotyczy to jabłek z zewnętrznych bloków

        std::cout << apples << '\n'; // wydrukuj wartość zewnętrznego bloku jabłka
    } // zewnętrzny blok jabłek zachowuje swoją wartość nawet po opuszczeniu zagnieżdżonego bloku

    std::cout << apples << '\n'; // wypisuje wartość zewnętrznych bloków jabłek

    return 0;
} // zewnętrzne jabłka blokowe zniszczone

Powyższy program wypisuje:

5
10
10

Wewnątrz zagnieżdżonego bloku nie ma możliwości bezpośredniego dostępu do cieniowanej zmiennej z bloku zewnętrznego.

Zacienianie zmiennych globalnych

Podobnie jak zmienne w bloku zagnieżdżonym mogą przesłaniać zmienne w bloku zewnętrznym, zmienne lokalne o tej samej nazwie co zmienna globalna będą przesłaniać zmienną globalną wszędzie tam, gdzie zmienna lokalna znajduje się w zasięgu:

#include <iostream>
int value { 5 }; // global variable

void foo()
{
    std::cout << "global variable value: " << value << '\n'; // wartość nie jest tutaj cieniowana, więc odnosi się to do wartości globalnej
}

int main()
{
    int value { 7 }; // ukrywa wartość zmiennej globalnej (gdziekolwiek w zakresie znajduje się wartość zmiennej lokalnej)

    ++value; // zwiększa wartość lokalną, a nie globalną

    std::cout << "local variable value: " << value << '\n';

    foo();

    return 0;
} // wartość lokalna jest niszczona

Ten kod wypisuje:

local variable value: 8
global variable value: 5

Ponieważ jednak zmienne globalne są częścią globalnej przestrzeni nazw, możemy użyć operatora zasięgu (::) bez przedrostka, aby poinformować kompilator, że mamy na myśli zmienną globalną, a nie zmienną lokalną.

#include <iostream>
int value { 5 }; // global variable

int main()
{
    int value { 7 }; // ukrywa zmienną globalną wartość
    ++value; // zwiększa wartość lokalną, a nie globalną

    --(::value); // zmniejsza wartość globalną, a nie lokalną (nawias dodano dla czytelności)

    std::cout << "local variable value: " << value << '\n';
    std::cout << "global variable value: " << ::value << '\n';

    return 0;
} // wartość lokalna jest niszczona

Ten kod wypisuje:

local variable value: 8
global variable value: 4

Unikaj zmiennego cieniowania

Ogólnie rzecz biorąc, należy unikać przesłaniania zmiennych lokalnych, ponieważ może to prowadzić do niezamierzonych błędów w przypadku użycia lub modyfikacji niewłaściwej zmiennej. Niektóre kompilatory wyświetlają ostrzeżenie, gdy zmienna jest cieniowana.

Z tego samego powodu, dla którego zalecamy unikanie cieniowania zmiennych lokalnych, zalecamy również unikanie cieniowania zmiennych globalnych. Można tego w prosty sposób uniknąć, jeśli wszystkie nazwy globalne używają przedrostka „g_”.

Najlepsza praktyka

Unikaj zmiennego cieniowania.

Dla użytkowników gcc

GCC i Clang wspierają flagę -Wshadow który wygeneruje ostrzeżenia, jeśli zmienna jest cieniowana. Istnieje kilka podwariantów tej flagi (-Wshadow=global, -Wshadow=local, i -Wshadow=compatible-local. Skonsultuj się z Dokumentacja GCC w celu wyjaśnienia różnic.

W programie Visual Studio takie ostrzeżenia są domyślnie włączone.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
85 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze