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;Должно быть без инициализации
Предварительное объявление глобальной переменной constextern const int g_x;Должно быть без инициализации
Предварительное объявление глобальной переменной constexprextern 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Внешнее связываниеВнутреннее связываниеГлобальная переменнаяДля начинающихЛокальная переменнаяОбласть видимостиОбучениеПрограммирование

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

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