Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
238
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

180 Часть 1 # Введение в програттшрошаишв ко С+Ф

Для сбрасывания отдельных бит в О, потребуются маски, где все биты, за иск­ лючением одного, установлены в 1. При использовании поразрядной логической операции "И" все биты, кроме нулевого бита в маске, остаются неизмененными. Чтобы сбросить бит 1, нужна маска с нулевым первым битом. Для сброса третьего бита потребуется маска, у которой третий бит установлен в 0. Чтобы сбросить бит 6, нужно применить маску с нулевым шестым битом и остальными битами, установленными в 1. Эти маски трудно выразить в виде десятичных или шестнадцатеричных констант. Кроме того, на разных платформах могут потребоваться маски разного размера, что влияет на переносимость кода. Очень часто для уста­ новки битов в 1 используют отрицание (инверсию). С помош,ью операции "И" можно сбросить эти биты в 0:

status

&= "CLEAR;

/ /

сброс бита 1

в О (если он равен 1)

status

&= "FULL;

/ /

сброс

бита

3

в О (если

он равен

1)

status

&= "EMPTY;

/ /

сброс

бита

6

в О (если

он равен

1)

Для доступа к значениям отдельных битов используется операция "И" с мас­ ками, где все биты установлены в О, за исключением одного, к которому нужно обратиться. Если данный бит установлен, то результат операции будет ненулевым (true), а если он установлен в О, то результатом будет О (false). С этими операция­ ми будут работать такие же маски, какие применялись для установки и сброса бита состояния:

int clear, f u l l , empty;

/ /

для проверки на true или false

clear

= status

& CLEAR;

/ /

true,

если бит 1 установлен в 1

f u l l

= status

& FULL;

/ /

true,

если

бит

3

установлен

в 1

empty = status

& EMPTY;

/ /

true,

если

бит

6

установлен

в 1

Эти операции нижнего уровня для упаковки и распаковки последовательностей битов (пример с адресацией) или отдельных битов (пример с состоянием) доста­ точно сложны, часто приводят к ошибкам, и их нельзя назвать интуитивно понят­ ными. C++ позволяет присваивать имена сегментам битов разного размера. Это делается с помои;ью обычного определения структуры. Для ка>вдого поля задает­ ся число битов (длина поля) — после двоеточия указывается неотрицательная константа:

struct

Address {

 

int

page : 4;

/ / не очень велико для 12 бит

int

offset : 12; }

Поля данных упакованы в целое. С целыми со знаком нужно быть поаккурат­ нее — один бит обычно отводится для знака. Если нужно использовать все выде­ ленные для поля биты, то поле должно быть беззнаковым:

struct Address {

 

unsigned

int

page : 4;

 

unsigned

int

offset : 12; }

/ / место для 12 бит

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

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

Глава 5 • Агрегирование с помощью типов, определяемых програ1^мистол^

| 181 g

Эти переменные определяются точно так же, как переменные-структуры. Доступ к битовым полям соответствует доступу к обычным полям структуры:

Address

а; unsigned address;

/ / должно инициализироваться

address

= ('а. page « 12) | а. offset;

 

Если нужно выделить для флага 1 бит, убедитесь, что это поле unsigned, а не signed. Поля не обязательно должны иметь имена. Неименованные поля исполь­ зуются для дополнения (но при этом все равно нужно задавать тип, указывать двоеточие и длину поля):

struct Status {

 

 

 

unsigned

: 1;

 

/ /

бит О

unsigned

Clear

: 1;

/ /

бит 1

unsigned

: 1;

 

/ /

бит 2

unsigned

Full

: 1;

/ /

бит 3

unsigned

: 2;

 

/ /

бит 4 и 5

unsigned

Empty

: 1;};

/ /

бит 6

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

Status Stat;

 

 

/ /

убедитесь в инициализации

int clear,

f u l l , empty;

 

 

/ /

для тестирования на true или false

Stat.Clear

= stat.FuU

= stat.Empty

= 1;

/ /

установить бит в 1

Stat.Clear

= stat.FuU

= stat.Empty

= 0;

/ /

сбросить биты в О

clear

= stat.Clear;

/ / значения могут быть протестированы

f u l l

= Stat.FuU;

 

empty = stat.Empty;

 

Допускается нулевая длина поля. Это указывает компилятору, что следующее поле нужно выровнять на границу целого. Разрешается смешивать данные различ­ ных целых типов. Переключение типа с одного размера на другой выделяет следуюш,ее поле в памяти. Как показывает приведенный ниже пример, неаккуратное использование битовых полей может привести к тому, что выделяемое простран­ ство не уменьшится (данный код написан на 16-разрядной машине, где для целых выделяется два байта):

struct

Waste {

 

 

 

long

f i r s t

:

2 ;

/ /

здесь выделяются все 4 байта

unsigned second : 2 ;

/ /

здесь добавляются еще два

char

third

: 1;

/ /

short начинается на четном адресе

short

fourth

: 1; } ;

/ /

всего 10 байт

На некоторых машинах поля присваиваются слева направо, а на других — справа налево (это называется обратный и прямой порядок байтов).

Для внутренних структур данных порядок полей не проблема, однако для ото­ бражения внутреннего определения данных, например буферов устройства вводавывода, имеет значение. Когда "внешние" данные поступают в одном формате, а компилятор использует другой, данные в битовых полях могут сохраняться не­ корректно.

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

г 182

Итоги

Чость ! « Введение в программирование на C-f+

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

Так как к полям структур можно обращаться с помощью отдельных имен полей, это относительно безопасно. Компоненты массивов доступны с помощью индексов, и программа С+4- не предусматривает защиты на этапе компиляции или выполнения от недопустимых значений индексов, что может легко привести к некорректным результатам или порче памяти — частый источник проблем для программистов. Это особенно относится к символьным массивам, где конец до­ пустимых данных задается завершающим нулем (нулевым терминатором).

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

sr^^6i^ 6

^^^ правление памятью: стек и динамически распределяемая область

Темы данной главы

^Область действия имени как средство кооперации

^Управление памятью: классы памяти

^Управление памятью: использование динамически распределяемой области

^Обмен данными с файлами на диске

^Итоги

1^ ^ предыдущей главе рассказывалось о средствах реализации структур

Ш•И^^анных, определяемых программистом. Массивы и структуры являются

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

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

Для получения гибких структур данных C++ позволяет программисту созда­ вать динамические массивы и связанные структуры данных, однако это вынуждает использовать указатели и увеличивает сложность программы. Когда требуется дополнительная память для динамических неименованных переменных, она берет­ ся из так называемой динамически распределяемой области (heap). Динами­ ческие переменные не имеют имен, и ссылаться на них нужно косвенно, через указатели. Тем самым за счет достаточно сложного динамического управления памятью обеспечивается более высокая гибкость.

I 184

Часть I ® Введение в орограг^мирование на

В данной главе рассказывается о методах C + + для управления стеком и ^г,^^^л мически распределяемой областью, о базовых приемах использования простран­ ства имен, экстента имен и динамического управления памятью с помощью указателей. Эти методы имеют ключевое значение для эффективного использо­ вания системных ресурсов, однако в неопытных руках динамическое управление памятью может легко привести к аварийному завершению работы системы, порче содержимого памяти, "ут^ечкам" памяти (когда системе в итоге не хватает памяти, потому что она правильно не освобождается). Некоторым программистам по вкусу мощные возможности и увлекательность динамического управления памятью, другие предпочитают применять указатели как можно меньше. Каковы бы ни были ваши личные предпочтения, нужно хорошо понимать принципы управления име­ нами и памятью, поддерживаемые в C+ + .

Перед обсуждением вопросов динамического управления памятью нужно по­ знакомиться с принципами области действия имен и классами памяти. Это важно для понимания особенностей управления памятью в C+ + . После рассмотрения вопросов динамического управления памятью мы перейдем к методам использова­ ния внешней памяти — файлам на диске. Хранение данных в файлах позволяет программе работать практически с неограниченными по объему наборами данных.

Внимание Эта большая глава содержит целый набор важных концепций

ипрактических методов программирования. Нельзя стать квалифицированным разработчиком программ C++ без освоения основных концепций

ипринципов управления файлами и ввода-вывода для обмена данными

сфайлами. Но можно изучать остальную часть C++, даже не став экспертом

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

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

Каждое определяемое программистом имя (идентификатор) имеет в программе на языке C + + лексическую область действия (или просто область действия).

"Лексическая" она потому, что ссылается на сегмент исходного кода, где имя известно и может использоваться, а "область действия", поскольку вне данного сегмента программного кода имя либо неизвестно, либо ссылается на что-то со­ вершенно другое. Элементы программы, имена которых имеют область действия,— это имена определяемых программистом типов данных, функций, параметров, пе­ ременных и меток. Такие имена можно использовать в выражениях, определениях, вызовах функций и т. д.

Лексические области действия в С+ +

Лексическая область действия — статическая характеристика имени. Она озна­ чает, что область действия определяется лексической структурой программы на этапе компиляции, а не поведением программы во время выполнения. В C + + есть следующие области действия:

Блока

Функции

Файла

Всей программы

Класса

Пространства имен

Глава 6 ^ Управление па1У1ятыо

^^^y^'^'y.y^FV^Yh]

185

В этой главе обсуждаются первые четыре области. О других двух будет расска­ зано после более детального ознакомления с принципами классов и пространств имен. Область действия блока ограничивают открывающая и закрывающая фи­ гурные скобки. Область действия функции также ограничивается открывающей и закрывающей фигурной скобкой. Разница между ними в том, что функция имеет параметры (и их имена известны в ее области действия). Вход в область действия функции осуществляется при выполнении программы, когда эта функция вызыва­ ется. Блок не вызывается, а выполняется после предшествующих ему операторов (если такие есть). Например, при итерации в цикле for область действия неимено­ ванного блока ограничивается фигурными скобками и при каждой итерации про­ грамма входит в эту область. При вызове функции getBalanceO вызывается (с помощью имени функции) область действия ее блока (эта функция приведена ниже в листинге 6.1):

for ( i

= 0; i < count; i++)

 

{ t o t a l

+= getBalance(a[i]); }

/ / накопление итога

Область действия файла ограничивается физическими границами файла. Она содержит определения типа, определения и описания переменных, определения и описания функций. Каждый листинг программы, использовавшийся в предыду­ щих главах, представлял собой листинг исходного файла в границах этого файла.

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

Конфликты имен в одной области действия

Конфликты имен внутри области действия в C++ не допускаются. Имя должно быть уникальным в той области действия, где оно описано. В языке С определяе­ мые программистом типы обычно формировали отдельную область действия. Это означает, что если имя использовалось для типа, оно могло применяться в той же области действия для переменной. Компилятор (и сопровождающий приложение программист) из контекста делали вывод, что означает имя — тип или переменную.

В C++ реализуется более строгий подход. Все определяемые программистом имена образуют отдельное пространство имен. Если имя определяется в области действия для каких-либо целей, оно должно быть в ней уникально, т. е. отличаться от всех объявленных (для каких-то целей) в той же области действия имен. Это означает, что, если, к примеру, count — имя переменной, то ни тип, ни функция, ни параметр, ни другая переменная не может называться так в той области дейст­ вия, где объявлена переменная count.

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

ипростоты написания программ. Когда разработчик или программист находит

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

вобласть действия переменную count, он должен посмотреть, нет ли в ней другого такого же имени.

Единственное исключение из этого правила — имена меток. Они не конфлик­ туют с именами переменных, параметрами или типами, описанными или известны­ ми в той же области действия. Так как метки применяются в C++ нечасто, от них программу не становится сложнее читать, но злоупотреблять ими не следует.

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

Данные загружены
800123456
800123123
800123333
1200
1500
1800 Остаток на счете в долл. 4500
Рис. 6 . 1 . Вывод программы из листинга 6.1
Л и с т и нг 6 . 1 .
Ц 186 I

Часть ! * Введение ^програ1У1^ирован1^а на C-fФ

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

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

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

Имена функций C++ могут определяться в файле, но не в блоке или в другой функции. Областью действия имен функций является программа, то есть имя функции должно быть уникально в проекте. Необходимость координации имен в масштабе проекта часто усложняет координацию в коллективе разработчиков. То же относится и к расширению существующих программ при их сопровождении: при добавлении новых имен функций возможны конфликты. Еще один потен­ циальный источник неприятностей, связанный с именами функций, это интеграция нескольких библиотек от разных поставщиков (или из прежних проектов). Часто подобная проблема может не проявляться, пока созданные разными программи­ стами файлы не будут скомпонованы на последних этапах разработки.

В листинге 6.1 приведен простой пример загрузки учетных данных, их отображения и вычисления балансовых сумм. Для про­ стоты данные здесь не вводятся с клавиатуры и не загружаются из внешнего файла или БД. (Этим мы займемся позднее.) Исполь­ зуется просто два массива num[] и amounts[], которые подставляют значения учетных и балансовых данных. Сами данные загружаются в бесконечном цикле while, пока не обнаруживается контрольное значение - 1 . Затем второй цикл выводит номера счетов, третий — показывает балансовые данные, а четвертый — балансовые суммы. Здесь применяются два определенных профаммистом типа — струк­ тура Account, целочисленный синоним Index и функция getBalance(). Они используются не столько в силу необходимости, сколько для иллюстрации области действия. Для простоты набор данных берет­ ся небольшой. Вывод программы представлен на рис. 6.1.

Демонстрация лексической области действия типов, параметров и переменных

#inclucle <iostream> using namespace std;

struct Account

{

 

// глобальное определение типа

long num;

 

 

 

double

bal;

}

;

 

double getBalance(Account

a)

{ double

t o t a l

= a.bal;

// total в независимой области действия

return

t o t a l ;

}

// возвращает а,bal (лучше)

int mainO

{

typedef int Index;

Index const MAX = 5; Index i, count = 0

Account a[MAX]; double total 0;

//локальное определение типа

//набор данных и итоговое значение

 

 

 

 

 

 

Глава 6 ^ Управление поглятью

| 187 щ

while (true)

 

 

// выход по контрольному значению

{ long num[MAX] = { 800123456, 800123123, 800123333, -1 } ;

 

double amounts[MAX] = { 1200, 1500, 1800 } ;

// данные для загрузки

 

if (num[count] == -1) break;

 

// найдено контрольное значение

 

a[count].num = num[count];

 

// загрузка данных

 

a[count].bal = amounts[count];

 

 

 

 

count++; }

 

 

 

 

 

cout

«

" Данные загружены\п\п";

 

 

 

 

for

( i

= 0;

i < count; i++)

 

 

 

 

{

long temp = a[i].num;

 

/ /

temp в независимой области действия

 

cout

«

tmp «

endl; }

 

/ /

вывод учетных номеров

 

for

( i

= 0;

i < count; i++)

 

 

 

 

{

double

temp = a [ i ] . b a l ;

 

/ /

temp в независимой области действия

 

cout

«

tmp «

endl; }

 

 

 

 

for

( i

= 0;

i < count; i++)

 

 

 

 

{

t o t a l

+= getBalance(a[i]); }

/ /

накопление t o t a l для балансов

cout «

endl < " Остаток на счете в долл."

« t o t a l

«

endl;

 

return

0;

 

 

 

 

 

 

Внимание Данная программа компилировалась с помощью последней версии 32-разрядного компилятора, поэтому нет необходимости указывать, что значения 800123456 и другие имеют тип long. 16-разрядным компилятором

эта программа компилироваться не будет. Аналогично примерам главы 5, здесь для значений использовался суффикс L (800123456L и т. д.).

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

Здесь тип Account действует во всем файле и известен с места его определения до конца исходного файла. Переменные типа Account могут определяться в любом месте этой области действия. Применение имени Account в данной области дей­ ствия для любых других действий, например как имени целого, некорректно:

int Account = 5;

/ / некорректное использование имени Account

Тип Index имеет область действия функции и известен от места его определе­ ния до закрывающей фигурной скобки функции main(). Переменные типа Index могут определяться в функции main(), но не в другой области действия, например в функции getBalanceO:

double getBalance(Account

а)

{ Index

z;

/ / синтаксическая ошибка: имя Index здесь неизвестно

return

a.bal; }

 

Функция getBalanceO действует в масштабе программы. Никакие другие объек­ ты в области действия программы не могут называться getBalance.

Лексическая область действия имен переменных более разнообразна. Пере­ менные C+-f могут определяться следующим образом:

Переменные блока. Определяются после открывающей фигурной скобки блока (или в теле блока) и видимы с точки определения до конца блока. В листинге 6.1 переменные блока представляют собой массивы amounts[] и num[], определенные в первом цикле функции main(), переменная temp определяется во втором цикле main(),

иснова переменная temp — в третьем цикле main().

188Часть I ^ Введение в програ.^

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

до закрывающей фигурной скобки функции. В листинге 6.1 переменными функциями являются i, count, MAX, а[] и total, определенные

в функции mainO, и переменная total, определенная в getBalanceO.

Формальные параметры функции. Определены в заголовке функции и видимы в любом месте в теле функции. Это означает, что имя параметра может конфликтовать с определенной в данной функции переменой. У функции getBalanceO из листинга 6.1 только один формальный параметр — а.

Глобальные переменные. Действуют в масштабе файла.

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

Имена полей структуры локальны для блока определения структуры. Это озна­ чает, что на них можно ссылаться (без уточнения) вне данной области действия. В листинге 6.1 имена полей num и bal известны только в определении структуры Account. Следовательно, написать bal = 0; в функции main() будет некорректно, поскольку имя bal в mainO неизвестно. Но с помощью селектора-точки на эти поля можно ссылаться во всей области действия переменных типа Account (где эти переменные известны и видимы). В листинге 6.1 это область действия функ­ ции mainO (где определен массив а[] типа Account) м область действия функции getBalanceO (где определен параметр типа Account). Поскольку C + + позволяет программистам определять переменные в любом месте области действия, важно убедиться, что имя не используется в области действия перед определением. В листинге 6.1 константа МАХ должна лексически предшествовать определению массива а[], amounts[] и num[] в функции main().

Использование одинаковых имен в независимых областях действия

Когда имена определяются в разных областях действия, они не конфликтуют друг с другом (за некоторым исключением).

Понятие "разные" требует в этом случае некоторых пояснений. Как эти облас­ ти действия должны соотноситься друг с другом, чтобы одно и то же имя могло использоваться в каждой из них с разными целями?

Два блока с непересекаюилимися областями действия (не имеющие общих операторов) считаются разными и независимыми друг от друга. Например, два неименованных блока, которые прямо или косвенно следуют друг за другом

вфайле или в области действия, независимы. Они позволяют определять и ис­ пользовать одно и то же имя для совершенно разных целей. Имена, определенные

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

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

Это же относится к блокам функции, определяющим переменные или пара­

метры с помощью одного

имени. Например, переменная total определяется

и в функции getBalanceO,

и в main(). Опять же, в функции getBalanceO это

можно было бы сделать с помощью локальной переменной, но такой пример хо­ рошо иллюстрирует принцип области действия.

Гдава 6 ^ Управление памятью

| 189 |

Аналогично имя а используется как параметр в функции getBalanceO и как массив в функции main(). Когда имена определяются в независимых областях действия, каждое из них известно в своей области, и в координации нет необхо­ димости.

Использование одинаковых имен во вложенных областях действия

Еще один тип разных областей действия — вложенные области. С+Н язык с блочной структурой. Это означает, что лексически его области действия могут быть вложенными, т. е. фигурные скобки одной области действия целиком нахо­ дятся в другой области действия. Заметим, что разные области действия либо независимы (одна начинается перед другой), либо вложены (одна внутри другой), но не пересекаются.

В большинстве программ C + + используются вложенные области действия. Неименованный блок может быть вложенным в другой неименованный блок или функцию. Нельзя вкладывать неименованный блок непосредственно в область действия файла, так как управление не будет передаваться в этот блок (требуется заголовок функции). Функция может быть вложенной только в области действия файла, но не в другой функции. Например, ниже делается попытка скрыть функ­ цию getBalanceO внутри main() — ее имя не будет конфликтовать с другими именами getBalance. Однако это ничего не дает: такие конструкции в C + + не допускаются.

int mainO

// недопускается в C++

{ double getBalance(Account a)

{ double total = a.bal;

 

return total; }

 

for (i = 0; i < count; i++)

// накопление total

{ total += getBalance(a[i]); }

cout « endl « "Total of balances $" «

total « endl;

return 0; }

 

В листинге 6.1 тело цикла реализовано как неименованные блоки. Они вло­ жены в область действия функции main(). Области действия функций main() и getBalanceO вложены в область действия исходного файла. По суидеству, область действия файла вложена в область действия программы.

Введение вложенных областей действия не изменяет правил видимости пере­ менных или типов, определенных во внешней области действия. Они видимы во вложенных областях. Например, переменная count известна от места ее определе­ ния до конца функции mainO, независимо от того, содержит ли функция main() вложенные циклы. Следовательно, когда неименованный вложенный блок в пер­ вом цикле main О ссылается на переменную count, то это ссылка на переменную, определенную во внешнем блоке. Аналогично ссылки на элементы массива а[] имеются во вложенных блоках во всех трех циклах. Переменная total определена в функции mainO, а ссылки на нее присутствуют во вложенных блоках третьего цикла.

Не допускается ссылка из внешней области действия на переменные, опреде­ ленные во вложенной области действия. Например, массивы num[] и amounts[] определены в блоке первого цикла функции main() и не могут использоваться в функции main О вне этого блока. Было бы некорректно написать второй цикл в листинге 6.1 следуюш,им образом, ссылаясь на num[] во внешнем цикле:

for (i

= 0; i < count; i++)

 

cout

« num[i] « endl;

/ / num[] неизвестно

Соседние файлы в предмете Программирование на C++