Время жизни и область видимости локальных объектов

Лекция 10

 

  Реализация метода пошаговой детализации с помощью пользовательских функций на С++.

Время жизни и область видимости локальных объектов

 

В лекции 7 мы говорили о методах разработки алгоритмов решения сложных задач и о теории структурного программирования. Напомним основные принципы этой теории:

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

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

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

· Повторяющиеся фрагменты алгоритма следует оформлять как вспомогательные, т.е. при программировании их следует оформлять в виде подпрограмм.

Там же, в лекции 7 был приведен пример разработки алгоритма с использованием метода пошаговой детализации и вспомогательных алгоритмов. Сейчас реализуем этот алгоритм на средствами языка программирования С++. Напомним условие задачи: требовалось вычислить периметр p и площадь s треугольника по заданным координатам трех его вершин: x 1, y 1; x 2, y 2; x 3, y 3.

Для решения задачи используются известные формулы:

 

Р = А + В + С;

S=  (формула Герона);

A = ; B = ;

C = ,

где Рр = Р/2 – полупериметр; A, B, C – длины сторон треугольника.

 

Была разработана следующая иерархия процедур (рис.1):

Рисунок 1 - Схема иерархии процедур для решения задачи

 

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

· функцию CalcP(), вычисляющую периметр треугольника по его сторонам;

· функцию CalcS(), вычисляющую площадь треугольника по его сторонам;

· функцию LenS (), вычисляющую длину отрезка по координатам двух точек;

· функцию PS(), вычисляющую периметр и площадь треугольника;

· функцию GetCoord() для ввода с клавиатуры координат вершин треугольника;

· функцию PutPS() для вывода вычисленных периметра и площади треугольника.

Функцию самого верхнего уровня CalcP s() заменим главной функцией main () языка С++.

Схемы разработанных алгоритмов представлены на рисунках 2-5.

 

 

Рисунок 2 – Схемы алгоритмов функцийCalcP() и CalcS()

Рисунок 3 - Схемы алгоритмов функцийLenS () и PS ( )

Рисунок 4 - Схемы алгоритмов функций GetCoord ()и PutPS ()

Рисунок 5 - Схема алгоритма главной функции main ()

 

Проекты, предназначенные для решения задач на С++ как правило, содержат несколько пользовательских функций и состоят из нескольких файлов исходного кода (с расширением .срр), что очень удобно при отладке программ. Принято объединять в отдельный файл функции для ввода исходных данных и вывода результатов, и также иметь отдельный файл (или несколько файлов) с функциями, которые непосредственно участвуют в решении задачи (а в больших проектах таких файлов может быть очень много). В небольших отдельных файлах проще искать ошибки, т.к. возможна их раздельная компиляция, а потом уже готовые файлы с отлаженными функциями легко включить в проект. То есть, функции объединяются по смыслу выполняемых действий в отдельные файлы - исходные модули (они имеют расширение .срр), компилируемые раздельно. Получившиеся в процессе компиляции файлы  - объектные модули  (файлы с расширением . obj), объединяются в исполняемую программу с помощью компоновщика (получают исполняемый файл с расширением . exe).

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

Программный код проекта представлен ниже на рисунках 6-8.

В первый файл с именем GetPut.cpp запишем функции ввода исходных данных GetCoord() и вывода результатов PutPS(). Обе эти функции типа void, так как не имеют возвращаемого значения. Функция ввода координат вершин треугольника GetCoord() имеет 6 выходных параметров (6 координат для трех вершин) и ни одного входного параметра. Функция PutPS(), предназначенная для вывода периметра и площади треугольника, имеет 2 входных параметра и ни одного выходного параметра.  Так как функции этого файла используют объекты cin и cout, то в нем присутствуют директивы #include <iostream> и using namespace std;.

 

Рисунок 6 - Программный код файла с функциями для ввода исходных данных и для вывода результатов

Во второй файл с именем Resh . cpp запишем функции, выполняющие необходимые вычисления для решения задачи.

Функции LenS (), CalcP() и CalcS() определены как функции с входными параметрами и возвращают один результат своей работы через возвращаемое значение оператором return.

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

Для вычисления квадратного корня в функциях этого файла в него необходимо включить библиотеку математических функций, поэтому в начале программного кода стоит директива препроцессора # include < cmath >.

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

Рисунок 7 - Программный код файла с функциями, выполняющими необходимые вычисления для решения задачи

 

В третьем файле с именем main . cpp будет находиться главная функция main(), программный код которой содержит только вызовы разработанных функций ввода исходных данных, решения задачи и вывода результатов. В этом файле присутствует директива препроцессора # include < iostream >, необходимая для команды задержки закрытия консольного окна system("PAUSE");. Также, до определения главной функции main() , в файл включены прототипы вызываемых в ней функций GetCoord (), PS () и PutPS (). Остальные используемые в проекте функции главную функцию не интересуют - это «внутренняя кухня».

 

 

Рисунок 8 - Программный код файла с главной функцией

 

Результаты выполнения проекта приведены на рис. 9:

 

Рисунок 9 - Результаты выполнения проекта

 

Снова обратим внимание на то, как создавать свои пользовательские функции. Перед созданием функции надо понять, какие ей нужны данные для работы, т.е. ЧТО она должна получить, и, если эти данные в результате работы функции должны остаться неизменными, то их следует оформлять как входные параметры, т.е. как формальные параметры, передающиеся по значению.

Далее, надо определить, ЧТО функция должна вернуть как результат своей работы, и если у нее один результат, то вернуть его через возвращаемое значение, а если несколько – то можно один вернуть через возвращаемое значение, остальные – оформить как выходные параметры, передающиеся по ссылке. Можно и все результаты оформить как выходные параметры, передающиеся по ссылке, а типом возвращаемого значения назначить void. Если никаких результатов возвращать не нужно, то выходных параметров нет, а сама функция имеет тип void .

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

 

Время жизни и область видимости локальных объектов

 

Объект, определенный в программе, обладает двумя свойствами. Это:

- «время жизни» объекта – это время, в течение которого система гарантирует сохранность информации, записанной в его области памяти;

- «область видимости» объекта – это область программы, в которой информация в памяти объекта доступна для обработки (для чтения и изменения).

Программа состоит из отдельных объектов – функций. Каждая функция – это независимая область памяти. Блок задается фигурными скобками { }. Тело функции ограничено блоком. Как уже говорилось, если объект–данное (переменная, константа и т.п.) определен в блоке, то такой объект называется локальным объектом

Локальный объект «живет» в блоке, то есть записанная в нем информация не будет разрушена, пока блок активен, пока выполняются его инструкции. Локальный объект, определенный в блоке, «жив», но может быть не виден в нем.

Локальный объект «виден» в блоке, то есть информация, записанная в нем, доступна для обработки только в области памяти этого блока от точки определения объекта в инструкции его определения до конца блока, в котором он определен.

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

На рис. 10 приведен пример кода с локальными объектами.

 

Рисунок 10 – Пример кода с локальными объектами

 

В этой функции fun «живут» локальные объекты a, b (два объекта),   c , x .

Объект a определен в параметре, который получен по значению. Его область видимости – все тело функции fun.

Объект b,   определенный в параметре, получен по значению.  Его область видимости – все тело функции fun, кроме области вложенного блока (желтое пространство). Так как в этом блоке определен другой объект с именем b . Этот объект «живет» в этом вложенном блоке.Его область видимости от точки определения до скобки, закрывающей этот блок. Этот объект b закрывает в этом месте объект b, определенный в параметре. Объект b, определенный в параметрах функции fun ,  во вложенном блоке «жив», информация в нем не испорчена, но не доступна.

Обратите внимание, что в инструкции return x + b ; виден и используется тот объект b, который определен в параметрах.

Объект х создан в инструкции определения float x;. Его область видимости от этой точки определения до конца блока, ограничивающее тело функции fun.

В операторе присваивания в выражении x = ( a * b + c ); неправомерно использован объект c . Он «живет» и виден только во внутреннем блоке функции.

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

 


Дата добавления: 2020-11-27; просмотров: 108; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!