Область действия идентификаторов

 

Каждый программный объект имеет область действия, которая определяется видом и местом его объявления. Существуют следующие области действия:

Блок.Идентификаторы, описанные внутри блока, являются локальными. Область их действия начинается в точке определения и заканчивается в конце блока, видимость — в пределах блока и внутренних блоков, время жизни — до выхода из блока (после выхода из блока память освобождается).

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

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

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

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

Файл.Идентификаторы, описанные вне любого блока, функции, класса или пространства имен, имеют глобальную видимость и постоянное время жизни и могут использоваться с момента их определения.

Поименованная область (пространство имен)

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

В разных пространствах имена могут совпадать, поскольку разрешение ссылок осуществляется по контексту идентификатора в программе, например:

struct Node{

int Node;

int i;

} Node;

В данном случае противоречия нет, поскольку имена типа, переменной и элемента структуры относятся к разным пространствам.

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

· имена переменных, функций, типов, определенных пользователем (typedef) и констант перечислений в пределах одной области видимости. Все они, кроме имен функций, могут быть переопределены во вложенных блоках;

· имена типов перечислений, структур, классов и объединений. Каждое имя должно отличаться от имен других типов в той же области видимости;

· элементы каждой структуры, класса и объединения. Имя элемента должно быть уникально внутри структуры, но может совпадать с именами элементов других структур;

· Метки образуют отдельное пространство имен.

Внешние объявления

Любая функция автоматически видна во всех модулях программы. Если требуется ограничить область действия функции файлом, в котором она описана, используется модификатор static.

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

Все описания одной и той же переменной должны быть согласованы.

Пример: описание двух глобальных переменных в файлах оnе.срр и two.cpp с помощью заголовочного файла my_header. h:

Файл my_header.h - внешние объявления

extern int а;

extern double b;

Файл оnе.срр

#include "my_header.h"

int a;

Файл two.cpp

#include "my_header.h"

double b;

Обе переменные доступны в файлах оnе.срр и two.cpp.

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

Поименованнные области (пространства имен)

Можно явным образом задать пространство имен с помощью оператора namespace.

Объявление пространства имен имеет вид:

namespace [ имя_области ]{ /* Объявления */ }

Если имя области не задано, компилятор определяет его самостоятельно с помощью уникального идентификатора, различного для каждого модуля.

Поименованные области служат для логического группирования объявлений и ограничения доступа к ним. При использовании единственной глобальной области видимости формировать программу из отдельных частей очень сложно из-за возможного совпадения и конфликта имен.

Механизм пространств имен вместе с директивой #include обеспечивают необходимую при написании больших программ гибкость путем сочетания логического группирования связанных величин и ограничения доступа.

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

Объявление объекта в неименованной области равнозначно его описанию как глобального с модификатором static (глобальные статические переменные видны только в том модуле, в котором они описаны). Помещать объявления в такую область полезно для того, чтобы сохранить локальность кода (нельзя получить доступ из одного файла к элементу неименованной области другого файла).

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

Пример:

namespace demo{

int i = 1;

int k = 0;

void funcl(int);

void func2(int) { /* ... */ }

}

namespace demo{ // Расширение

int i = 2; // Неверно - двойное определение

void func1(double); // Перегрузка

void func2(int); // Верно - повторное объявление

}

Объекты, объявленные внутри области, являются видимыми с момента объявления. К ним можно явно обращаться с помощью имени области и оператора доступа к области видимости ::, например:

demo::i = 100; demo::func2(10);

В объявлении поименованной области могут присутствовать как объявления, так и определения, но логично помещать в нее только объявления, а определять их позднее с помощью имени области и оператора доступа к области видимости ::, например:

void demo::funс1(int) { / * ... * / }

Это необходимо для разделения интерфейса и реализации. Таким способом нельзя объявить новый элемент пространства имен.

Если имя часто используется вне своего пространства, можно объявить его доступным с помощью оператораusing:

using demo::i:

После этого можно использовать имя без явного указания области.

Если требуется сделать доступными все имена из какой-либо области, используется оператор using namespace:

using namespace demo;

Операторы using и using namespace можно использовать и внутри объявления поименованной области, чтобы сделать в ней доступными объявления из другой области:

namespace Department_of_App1ied_Mathematics{

using demo::i;

...

}

Имена, объявленные в поименованной области явно или с помощью оператора using, имеют более высокий приоритет по отношению к именам, объявленным с помощью оператора using namespace. Это имеет значение при включении нескольких поименованных областей, содержащих совпадающие имена.

Короткие имена пространств имен могут войти в конфликт друг с другом, а длинные непрактичны при написании реального кода, поэтому допускается вводить синонимы имен:

namespace DAM = Department_of_Applied_Mathematics;