Область видимости объектов

Последнее обновление: 15.02.2023

Все переменные имеют определенное время жизни (lifetime) и область видимости (scope). Время жизни начинается с момента определения переменной и длится до ее уничтожения. Область видимости представляет часть программы, в пределах которой можно использовать объект. Как правило, область видимости ограничивается блоком кода, который заключается в фигурные скобки. В зависимости от области видимости создаваемые объекты могут быть глобальными, локальными или автоматическими.

Глобальные объекты

Глобальные переменные определены в файле программы вне любой из функций или любого другого блока кода и могут использоваться любой функцией. Глобальные переменные существуют в течение всей жизни программы и уничтожаются лишь с завершением программы.

Если глобальные переменные не инициализированы, то они получают нулевые значения.

Например, определим и используем глобальную переменную:

#include <iostream>

int n{5};   // глобальная переменная

void print()
{
    n++;
    std::cout << "n=" << n << std::endl;
}
 
int main()
{
    print();                        // n=6
    n++;
    std::cout << "n=" << n << std::endl;   // n=7
}

Здесь переменная n является глобальной и доступна из любой функции. При этом любая функция может изменить ее значение.

Локальные объекты

Объекты, которые создаются внутри блока кода (он может представлять функцию или какую-либо конструкцию типа циклов), называются локальными. Такие объекты доступны в пределах только того блока кода, в котором они определены.

Автоматические объекты

Локальные объекты, которые существуют только во время выполнения того блока, в котором они определены, являются автоматическими.

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

#include <iostream>

void print()
{
    int n {5};  // локальная переменная, которая существует только в функции print
    std::cout << "n=" << n << std::endl;
    //так сделать нельзя, так как m определена в функции main
    // std::cout << "m=" << m << std::endl;
}
int main()
{
    int m {2};  // локальная переменная, которая существует только в функции main 
    std::cout << "m=" << m << std::endl;
    //так сделать нельзя, так как n определена в функции print
    // std::cout << "n=" << n << std::endl;
}

Здесь в функции print определена локальная переменная n. В функции main определена автоматическая переменная m. Вне своих функций эти переменные недоступны. Например, мы не можем использовать переменную n в функции main, так как ее область видимости ограничена функцией print. Соответственно также мы не можем использовать переменную m в функции print, так как эта переменная ограничена фукцией main.

Подобным образом с помощью блока кода можно определить вложенные области видимости:

#include <iostream>

int main()
{
    int n1 {2};  // область видимости - вся функция main 

    {
        int n2 {5};     // область видимости - блок кода
        std::cout << "n2=" << n2 << std::endl;
        n1++;   // переменная n1 доступна, т.к. определена во внешнем контексте - функции
    }   // конец блока функции - конец времени жизни переменной n2

    // так нельзя - переменная n2 из блока функции уже не существует
    // std::cout << "n2=" << n2 << std::endl;
    // переменная n1 доступна до конца функции
    std::cout << "n1=" << n1 << std::endl;
}   // конец блока функции - конец времени жизни переменной n1

Для каждой области видимости доступны все те объекты, которые определены во внешней области видимости или во внешнем контексте. Глобальная область видимости является внешней для функции, поэтому функция может использовать глобальные переменные. А фукция является внешним контекстом для вложенного блока кода, поэтому блок кода может использовать переменную n1, которая определена в функции вне этого блока. Однако переменные, определенные в блоке кода, вне этого блока использовать нельзя.

Скрытие объектов

Локальные объекты, определенные внутри одного контекста, могут скрывать объекты с тем же именем, определенные во внешнем контексте:

#include <iostream>

int n {5};

int main()
{
	int n {10};
	std::cout << "n=" << n << std::endl;	// n=10
	
	{
		int n {20};
		std::cout << "n=" << n << std::endl; // n=20
	}
}

Здесь определено три переменных с именем n. Переменная n, определенная на уровне функции main (int n = 10;) скрывает глобальную переменную n. А переменная n, определенная на уровне блока, скрывает переменную, определенную на уровне функции main.

Однако иногда бывает необходимо обратиться к глобальной переменной. В этом случае для обращения именно к глобальной переменной можно использовать оператор :: перед именем переменной

#include <iostream>

int n {5};

int main()
{
	int n {10};
	std::cout << "n=" << ::n << std::endl;	// n=5
	
	{
		int n {20};
		std::cout << "n=" << ::n << std::endl; // n=5
	}
}

Статические объекты

Кроме автоматических есть особый тип локальных объектов - статические объекты. Они определяются на уровне функций с помощью ключевого слова static. Если автоматические переменные определяются и инициализируются при каждом входе в функцию, то статические переменные инициализируются только один раз, а при последующих вызовах функции используется старое значение статической переменной. То есть разница между локальными автоматическими и локальными статическими переменными состоит во времени жизни: автоматические переменные существуют до конца выполнения блока кода, а статические - до конца выполнения программы.

Например, пусть у нас будет функция со стандартной автоматической переменной:

#include <iostream>

void print()
{
    int n {1};
    std::cout << "n=" << n << std::endl;
    n++;
}
 
int main()
{
    print();
    print();
    print();
}

Функция print вызывается три раза, и при каждом вызове программа повторно будет выделять память для переменной n, которая определена в функции. А после завершения работы print, память для переменной n будет освобождаться. Соответственно ее значение при каждом вызове будет неизменно:

n=1
n=1
n=1

Теперь сделаем переменную n статической:

#include <iostream>

void print()
{
    static int n {1};
    std::cout << "n=" << n << std::endl;
    n++;
}
 
int main()
{
    print();
    print();
    print();
}

К переменной был добавлено ключевое слово static, поэтому при завершении работы функции print переменная не уничтожается, ее память не очищается, наоборот, она сохраняется в памяти. И соответственно результат работы программы будет иным:

n=1
n=2
n=3
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850