6.11 – Резюме об области видимости, продолжительности и связывании
Понятия области видимости, продолжительности и связывания вызывают много путаницы, поэтому мы собираемся занять дополнительный урок, чтобы всё это резюмировать. Некоторые из этих тем мы еще не рассмотрели, и они здесь только для полноты и для справочного материала, к которому можно будет обратиться позже.
Резюме об области видимости
Область видимости идентификатора определяет, где в исходном коде идентификатор может быть доступен.
- переменные с областью видимости блока / локальной областью видимости могут быть доступны только в том блоке, в котором они объявлены (включая вложенные блоки). Это включает в себя:
- локальные переменные;
- параметры функции;
- определения пользовательских типов (например, перечисления и классы), объявленные внутри блока;
- переменные и функции с глобальной областью видимости / областью видимости файла могут быть доступны в любом месте файла. Это включает в себя:
- глобальные переменные;
- функции;
- определения пользовательских типов (например, перечисления и классы), объявленные внутри пространства имен или в глобальной области видимости.
Резюме о продолжительности
Продолжительность переменной определяет, когда она создается и уничтожается.
- переменные с автоматической продолжительностью создаются в точке определения и уничтожаются при выходе из блока, частью которого они являются. Например, это:
- локальные переменные;
- параметры функции;
- переменные со статической продолжительностью создаются при запуске программы и уничтожаются при завершении программы. Например, это:
- глобальные переменные;
- статические локальные переменные;
- переменные с динамической продолжительностью создаются и уничтожаются по запросу программиста. Например, это:
- динамически создаваемые переменные.
Резюме о связывании
Связывание идентификатора определяет, относятся ли несколько объявлений идентификатора к одному и тому же идентификатору или нет.
- идентификатор без связывания означает, что идентификатор ссылается только сам на себя. Например:
- локальные переменные;
- определения пользовательских типов (например, перечисления и классы), объявленные внутри блока;
- к идентификатору с внутренним связыванием можно получить доступ в любом месте файла, в котором он объявлен. Например:
- статические глобальные переменные (инициализированные или неинициализированные);
- статические функции;
- константные глобальные переменные;
- функции, объявленные внутри безымянного пространства имен;
- определения пользовательских типов (таких как перечисления и классы), объявленные внутри безымянного пространства имен;
- к идентификатору с внешним связыванием можно получить доступ в любом месте файла, в котором он объявлен, или из других файлов (через предварительное объявление). Например:
- функции;
- неконстантные глобальные переменные (инициализированные или неинициализированные);
- глобальные переменные
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 активных спецификатора класса хранения:
Спецификатор | Значение | Примечание |
---|---|---|
extern | Статическая (или thread_local ) продолжительность хранения и внешнее связывание | |
static | Статическая (или thread_local ) продолжительность хранения и внутреннее связывание | |
thread_local | Продолжительность локального хранилища потока | Добавлено в C++11 |
mutable | Объект может быть изменен, даже если содержащий его класс является константным | |
auto | Автоматическая продолжительность хранения | Устарело в C++11 |
register | Автоматическая продолжительность хранения и подсказка компилятору поместить в регистр | Устарело в C++17 |
Термин «спецификатор класса хранения» обычно используется только в официальной документации.