Сделай Сам Свою Работу на 5

Локальная область видимости





Локальная область видимости – это часть исходного текста программы, содержащаяся в определении функции (или блоке внутри тела функции). Все функции имеют свои локальные области видимости. Каждая составная инструкция (или блок) внутри функции также представляет собой отдельную локальную область. Такие области могут быть вложенными. Например, следующее определение функции содержит два их уровня (функция выполняет двоичный поиск в отсортированном векторе целых чисел):

const int notFound = -1; // глобальная область видимости int binSearch( const vector<int> &vec, int val ) { // локальная область видимости: уровень #1 int low = 0; int high = vec.size() - 1;   while ( low <= high ) { // локальная область видимости: уровень #2 int mid = ( low + high ) / 2; if ( val < vec[ mid ] ) high = mid - 1; else low = mid + 1; } return notFound; // локальная область видимости: уровень #1

}

Первая локальная область видимости – тело функции binSearch(). В ней объявлены параметры функции vec и val, а также переменные low и high. Цикл while внутри функции задает вложенную локальную область, в которой определена одна переменная mid. Параметры vec и val и переменные low и high видны во вложенной области. Глобальная область видимости включает в себя обе локальных. В ней определена одна целая константа notFound.



Имена параметров функции vec и val принадлежат к первой локальной области видимости тела функции, и в ней использовать те же имена для других сущностей нельзя. Например:

int binSearch( const vector<int> &vec, int val ) { // локальная область видимости: уровень #1 int val; // ошибка: неверное переопределение val

// ...

Имена параметров употребляются как внутри тела функции binSearch(), так и внутри вложенной области видимости цикла while. Параметры vec и val недоступны вне тела функции binSearch().

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

Из-за порядка просмотра областей видимости в процессе разрешения имен объявление из внешней области может быть скрыто объявлением того же имени во вложенной области. Если бы в предыдущем примере переменная low была объявлена в глобальной области видимости перед определением функции binSearch(), то использование low в локальной области видимости цикла while все равно относилось бы к локальному объявлению, скрывающему глобальное:



int low; int binSearch( const vector<int> &vec, int val ) { // локальное объявление low // скрывает глобальное объявление int low = 0; // ... // low - локальная переменная while ( low <= high ) {//... } // ...

}

Для некоторых инструкций языка C++ разрешено объявлять переменные внутри управляющей части. Например, в цикле for переменную можно определить внутри инструкции инициализации:

for ( int index = 0; index < vecSize; ++index ) { // переменная index видна только здесь if ( vec[ index ] == someValue ) break; } // ошибка: переменная index не видна

if ( index != vecSize ) // элемент найден

Подобные переменные видны только в локальной области самого цикла for и вложенных в него (это верно для стандарта С++, в предыдущих версиях языка поведение было иным). Компилятор рассматривает это объявление так же, как если бы оно было записано в виде:

// представление компилятора { // невидимый блок int index = 0; for ( ; index < vecSize; ++index ) { // ... }

}

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

int index = 0; for ( ; index < vecSize; ++index ) { // ... } // правильно: переменная index видна

if ( index != vecSize ) // элемент найден



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

void fooBar( int *ia, int sz ) { for (int i=0; i<sz; ++i) ... // правильно for (int i=0; i<sz; ++i) ... // правильно, другое i for (int i=0; i<sz; ++i) ... // правильно, другое i

}

Аналогично переменная может быть объявлена внутри условия инструкций if и switch, а также внутри условия циклов while и for. Например:

if ( int *pi = getValue() ) { // pi != 0 -- *pi можно использовать здесь int result = calc(*pi); // ... } else { // здесь pi тоже видна // pi == 0 cout << "ошибка: getValue() завершилась неудачно" << endl;

}

Переменные, определенные в условии инструкции if, как переменная pi, видны только внутри if и соответствующей части else, а также во вложенных областях. Значением условия является значение этой переменной, которое она получает в результате инициализации. Если pi равна 0 (нулевой указатель), условие ложно и выполняется ветвь else. Если pi инициализируется любым другим значением, условие истинно и выполняется ветвь if. (Инструкции if, switch, for и while рассматривались в главе 5.)

Упражнение 8.1

Найдите различные области видимости в следующем примере. Какие объявления ошибочны и почему?

int ix = 1024; int ix() ;   void func( int ix, int iy ) { int ix = 255;   if (int ix=0) { int ix = 79; { int ix = 89; } } else { int ix = 99; }

}

Упражнение 8.2

К каким объявлениям относятся различные использования переменных ix и iy в следующем примере:

int ix = 1024;   void func( int ix, int iy ) { ix = 100;   for( int iy = 0; iy < 400; iy += 100 ) { iy += 100; ix = 300; } iy = 400;

}

Глобальные объекты и функции

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

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

Объявления и определения

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

// объявление функции calc() // определение находится в другом файле void calc(int); int main() { int loc1 = get(); // ошибка: get() не объявлена calc(loc1); // правильно: calc() объявлена // ...

}

Определение объекта имеет две формы:

type_specifier object_name;

type_specifier object_name = initializer;

Вот, например, определение obj1. Здесь obj1 инициализируется значением 97:

int obj1 = 97;

Следующая инструкция задает obj2, хотя начальное значение не задано:

int obj2;

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

int var1 = 0;

int var2;

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

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

extern int i;

Эта инструкция “обещает”, что в программе имеется определение, подобное

int i;

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

// заголовочный файл extern int obj1; extern int obj2; // исходный файл int obj1 = 97;

int obj2;

Объявление глобального объекта с указанием ключевого слова extern и с явной инициализацией считается определением. Под этот объект выделяется память, и другие определения не допускаются:

extern const double pi = 3.1416; // определение

const double pi; // ошибка: повторное определение pi

Ключевое слово extern может быть указано и при объявлении функции – для явного обозначения его подразумеваемого смысла: “определено в другом месте”. Например:

extern void putValues( int*, int );

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.