6.11 – Резюме об области видимости, продолжительности и связывании

Добавлено15 мая 2021 в 15:31

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

Резюме об области видимости

Область видимости идентификатора определяет, где в исходном коде идентификатор может быть доступен.

  • переменные с областью видимости блока / локальной областью видимости могут быть доступны только в том блоке, в котором они объявлены (включая вложенные блоки). Это включает в себя:
    • локальные переменные;
    • параметры функции;
    • определения пользовательских типов (например, перечисления и классы), объявленные внутри блока;
  • переменные и функции с глобальной областью видимости / областью видимости файла могут быть доступны в любом месте файла. Это включает в себя:
    • глобальные переменные;
    • функции;
    • определения пользовательских типов (например, перечисления и классы), объявленные внутри пространства имен или в глобальной области видимости.

Резюме о продолжительности

Продолжительность переменной определяет, когда она создается и уничтожается.

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

Резюме о связывании

Связывание идентификатора определяет, относятся ли несколько объявлений идентификатора к одному и тому же идентификатору или нет.

  • идентификатор без связывания означает, что идентификатор ссылается только сам на себя. Например:
    • локальные переменные;
    • определения пользовательских типов (например, перечисления и классы), объявленные внутри блока;
  • к идентификатору с внутренним связыванием можно получить доступ в любом месте файла, в котором он объявлен. Например:
    • статические глобальные переменные (инициализированные или неинициализированные);
    • статические функции;
    • константные глобальные переменные;
    • функции, объявленные внутри безымянного пространства имен;
    • определения пользовательских типов (таких как перечисления и классы), объявленные внутри безымянного пространства имен;
  • к идентификатору с внешним связыванием можно получить доступ в любом месте файла, в котором он объявлен, или из других файлов (через предварительное объявление). Например:
    • функции;
    • неконстантные глобальные переменные (инициализированные или неинициализированные);
    • глобальные переменные extern const;
    • глобальные переменные inline const;
    • определения пользовательских типов (например, перечисления и классы), объявленные внутри пространства имен или в глобальной области видимости.

Идентификаторы с внешним связыванием обычно вызывают ошибку компоновщика из-за повторяющихся определений, если определения скомпилированы в более чем одном файле .cpp (из-за нарушения правила одного определения). Из этого правила есть некоторые исключения (для типов, шаблонов, встраиваемых функций и переменных) – мы рассмотрим их подробнее в будущих уроках, когда будем говорить об этих темах.

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

Краткое резюме об области видимости, продолжительности и связывании переменных

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

Краткое резюме об области видимости, продолжительности и связывании переменных
Тип Пример Область видимости Продолжительность Связывание Примечания
Локальная переменная int x; Блок Автоматическая Нет  
Статическая локальная переменная static int s_x; Блок Статическая Нет  
Динамическая переменная int *x { new int{} }; Блок Динамическая Нет  
Параметр функции void foo(int x) Блок Автоматическая Нет  
Внешняя неконстантная глобальная переменная int g_x; Файл Статическая Внешнее Либо инициализируется, либо не инициализируется
Внутренняя неконстантная глобальная переменная static int g_x; Файл Статическая Внутреннее Либо инициализируется, либо не инициализируется
Внутренняя неконстантная глобальная переменная constexpr int g_x { 1 }; Файл Статическая Внутреннее Должна быть инициализирована
Внешняя константная глобальная переменная extern constexpr int g_x { 1 }; Файл Статическая Внешнее Должна быть инициализирована
Встраиваемая константная глобальная переменная inline constexpr int g_x { 1 }; Файл Статическая Внешнее Должна быть инициализирована
Внутренняя константная глобальная переменная const int g_x { 1 }; Файл Статическая Внутреннее Должна быть инициализирована
Внешняя константная глобальная переменная extern const int g_x { 1 }; Файл Статическая Внешнее Должна быть инициализирована
Встраиваемая константная глобальная переменная inline const int g_x { 1 }; Файл Статическая Внешнее Должна быть инициализирована

Краткое резюме о предварительном объявлении

Вы можете использовать предварительное объявление для доступа к функции или переменной в другом файле:

Краткое резюме о предварительном объявлении
Тип Пример Примечания
Предварительное объявление функции void foo(int x); Только прототип, без тела функции
Предварительное объявление неконстантной глобальной переменной extern int g_x; Должно быть без инициализации
Предварительное объявление глобальной переменной const extern const int g_x; Должно быть без инициализации
Предварительное объявление глобальной переменной constexpr extern constexpr int g_x; Не допускается, переменная constexpr не может быть предварительно объявлена

Так что же такое спецификатор класса хранения?

При использовании как части объявления идентификатора ключевые слова static и extern называются спецификаторами класса хранения. В этом контексте они устанавливают продолжительность хранения и связывание идентификатора.

C++ поддерживает 4 активных спецификатора класса хранения:

Спецификаторы класса хранения в C++
Спецификатор Значение Примечание
extern Статическая (или thread_local) продолжительность хранения и внешнее связывание  
static Статическая (или thread_local) продолжительность хранения и внутреннее связывание  
thread_local Продолжительность локального хранилища потока Добавлено в C++11
mutable Объект может быть изменен, даже если содержащий его класс является константным  
auto Автоматическая продолжительность хранения Устарело в C++11
register Автоматическая продолжительность хранения и подсказка компилятору поместить в регистр Устарело в C++17

Термин «спецификатор класса хранения» обычно используется только в официальной документации.

Теги

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