6.5 – Затенение переменных (скрытие имен)

Добавлено 11 мая 2021 в 13:06

Каждый блок определяет свою собственную область видимости. Итак, что происходит, когда у нас есть переменная внутри вложенного блока, имя которой совпадает с именем переменной во внешнем блоке? Когда это происходит, вложенная переменная «скрывает» внешнюю переменную в областях, где они обе находятся в области видимости. Это называется скрытием имен или затенением.

Затенение локальных переменных

#include <iostream>
 
int main()
{ // внешний блок
    int apples { 5 }; // здесь apples внешнего блока
 
    { // вложенный блок
        // apples здесь относится к apples внешнего блока
        std::cout << apples << '\n'; // выводим значение apples внешнего блока
 
        int apples{ }; // определяем apples в области видимости вложенного блока
 
        // apples теперь относятся к apples вложенного блока
        // переменная apples внешнего блока временно скрыта
 
        apples = 10; // this assigns value 10 to nested block apples, not outer block apples
 
        std::cout << apples << '\n'; // выводим значение apples вложенного блока
    } // apples вложенного блока уничтожается
 
 
    std::cout << apples << '\n'; // выводим значение apples внешнего блока
 
    return 0;
} // apples внешнего блока уничтожается

Если вы запустите эту программу, она напечатает:

5
10
5

В приведенной выше программе мы сначала объявляем переменную с именем apples во внешнем блоке. Эта переменная видна во внутреннем блоке, что мы можем увидеть, распечатав ее значение (5). Затем мы объявляем другую переменную (также с именем apples) во вложенном блоке. С этого момента и до конца блока имя apples относится к apples вложенного блока, а не к apples внешнего блока.

Таким образом, когда мы присваиваем apples значение 10, мы присваиваем его переменной вложенного блока. После печати этого значения (10) переменная apples вложенного блока уничтожается. Существование и значение переменной apples внешнего блока не затрагивается, и мы доказываем это, печатая значение apples внешнего блока (5).

Обратите внимание, что если бы переменная apples вложенного блока не была определена, имя apples во вложенном блоке относилось бы к apples внешнего блока, и поэтому присвоение значения 10 применилось бы к переменной apples внешнего блока:

#include <iostream>
 
int main()
{ // внешний блок
    int apples{5}; // здесь apples внешнего блока
 
    { // вложенный блок
        // apples здесь относится к apples внешнего блока
        std::cout << apples << '\n'; // выводим значение apples внешнего блока
 
        // в этом примере во внутреннем блоке apples не определена
 
        apples = 10; // это относится к apples внешнего блока
 
        std::cout << apples << '\n'; // выводим значение apples внешнего блока
    } // apples внешнего блока сохраняет свое значение даже после того, 
      // как мы выходим из вложенного блока
 
    std::cout << apples << '\n'; // выводим значение apples внешнего блока
 
    return 0;
} // apples внешнего блока уничтожается

Приведенная выше программа напечатает:

5
10
10

Находясь внутри вложенного блока, нет возможности напрямую получить доступ к затененной переменной внешнего блока.

Затенение глобальных переменных

Подобно тому, как переменные во вложенном блоке могут затенять переменные внешнего блока, локальные переменные с тем же именем, что и глобальная переменная, будут затенять глобальную переменную везде, где локальная переменная находится в области видимости:

#include <iostream>
int value { 5 }; // глобальная переменная
 
void foo()
{
    // value здесь не затенено, поэтому это относится к глобальной value
    std::cout << "global variable value: " << value << '\n';
}
 
int main()
{
    int value { 7 }; // скрывает глобальную переменную value до конца этого блока
 
    ++value; // инкрементирует локальную value, а не глобальную
 
    std::cout << "local variable value: " << value << '\n';
 
    foo();
 
    return 0;
} // локальная переменная value уничтожается

Этот код напечатает:

local variable value: 8
global variable value: 5

Однако, поскольку глобальные переменные являются частью глобального пространства имен, мы можем использовать оператор области видимости (::) без префикса, чтобы сообщить компилятору, что мы имеем в виду глобальную переменную, а не локальную.

#include <iostream>
int value { 5 }; // глобальная переменная
 
int main()
{
    int value { 7 }; // скрывает глобальную переменную value
    ++value;         // инкрементирует локальную value, а не глобальную
 
    --(::value); // декрементирует глобальную value, а не локальную
                 // (скобки добавлены для читаемости)
 
    std::cout << "local variable value: " << value << '\n';
    std::cout << "global variable value: " << ::value << '\n';
 
    return 0;
} // локальная переменная value уничтожается

Этот код напечатает:

local variable value: 8
global variable value: 4

Избегайте затенения переменных

Как правило, следует избегать затенения локальных переменных, поскольку это может привести к непреднамеренным ошибкам при использовании или изменении неправильной переменной. Некоторые компиляторы выдают предупреждение, когда переменная затеняется.

По той же причине, по которой мы рекомендуем избегать затенения локальных переменных, мы также рекомендуем избегать затенения глобальных переменных. Этого легко избежать, если все ваши глобальные имена используют префикс "g_".

Лучшая практика


Избегайте затенения переменных.

Теги

C++ / CppLearnCppnamespaceГлобальная переменнаяДля начинающихЛокальная переменнаяОбласть видимостиОбучениеПрограммированиеПространство имен

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.