Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Глобальные переменные; будьте начеку



 

Следует отметить, что в C++ глобальные переменные почти никогда не используются. Язык C++ вырос из С, где использование глобальных переменных всегда было чревато возникновением ошибок, хотя обойтись без их применения также было не возможно. Глобальные переменные необходимы в тех случаях, когда программисту нужно сделать данные доступными для многих функций, а передавать данные из функции в функцию как параметры проблематично.

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

На занятии 14 вы познакомитесь с мощной альтернативой использованию глобальных переменных, которая предусмотрена в C++, но недоступна в языке С.

 

Подробнее о локальных переменных

 

О переменных, объявленных внутри функции, говорят, что они имеют локальную область видимости. Это означает, как упоминалось выше, что они видимы и пригодны для использования только в пределах той функции, в которой определены. Фактически в C++ можно определять переменные в любом месте внутри функции, а не только в ее начале. Областью видимости переменной является блок, в котором она определена. Таким образом, если в теле функции будет блок, выделенный парой фигурных скобок, и в этом блоке объявляется переменная, то она будет доступна только в пределах блока, а не во всей функции, как показано в листинге 5.4.

Листинг 5.4. Видимиость локальных переменных

1: // Листинг 5.4. Видимость переменных,

2: // обьявленных внутри блока

3:

4: #include < iostream.h>

5:

6: void nyFunc();

7:

8: int main()

9: {

10: int x = 5;

11: cout < < " \nIn main x is: " < < x;

12:

13: myFunc();

14:

15: cout < < " \n8ack in main, x ts: " < < x;

16: return 0;

17: }

18:

19: void myFunc()

20: {

21:

22: int x = 6;

23: cout < < " \nIn myFunc. local x: " < < x < < endl;

24:

25: {

26: cout < < " \nIn block in myFunc, x is: " < < x;

27:

28: int x = 9;

29:

30: cout < < " \nVery local x: " < < x;

31: }

32:

33: cout < < " \nOut of block, in myFunc, x: " < < x < < endl;

34: }

 

Результат:

In main x is: 5

In myFunc, local x: 8

In block in myFunc, x is

Very local x; 9

Out of block, in myFunc,

Back in main, x is: 5

 

Анализ: Эта программа начинается с инициализации локальной переменной x в функции main() (строка 10). Выведенное в строке 11 значение переменной x позволяет убедиться, что переменная х действительно была инициализирована числом 5.

Затем в программе вызывается функция MyFunc(), в теле которой в строке 22 объ­является локальная переменная с тем же именем x и инициализируется значением 8. Это значение выводится на экран в строке 23.

Блок, заключенный в фигурные скобки, начинается в строке 25, и в строке 26 сно­ва выводится значение локальной переменной x. Но в строке 28 создается новая пе­ременная с таким же именем x, которая является локальной по отношению к данному блоку. Эта переменная тут же инициализируется значением 9.

Значение последней созданной переменной x выводится на экран в строке 30. Ло­кальный блок завершается строкой 31, и переменная, созданная в строке 28, выходит за пределы видимости и удаляется из памяти.

В строке 33 на экран выводится значение той переменной x, которая была объяв­лена в строке 22. На нее никоим образом не повлияло определение новой переменной x в строке 28, и ее значение по-прежнему равно 8.

В строке 34 заканчивается область видимости функции MyFunc() и ее локальная пе­ременная x становится недоступной. Управление программой возвращается к стро­ке 15, в которой выводится значение локальной переменной -x, созданной в строке 10. Вы сами можете убедиться в том, что на нее не повлияла ни одна из одноименных переменных, определенных в функции MyFunc().

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

 

Операторы, используемые в функциях

 

Фактически на количество или типы операторов, используемых в функциях, ника­ких ограничений не накладывается. И хотя внутри функции нельзя определить другую функцию, но из одной функции можно вызывать сколько угодно других функций; именно этим и занимается функция main() почти в каждой программе C++. Более того, функции могут вызывать даже самих себя (эта ситуация рассматривается в раз­деле, посвященном рекурсии).

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

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

 

Подробнее об аргументах функций

 

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

Аргументом функции может быть любое действительное выражение C++, включающее константы, математические и логические выражения и другие функции, которые возвращают некоторое значение.

Использование функций в качестве параметров функций

 

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

В качестве примера предположим, что у вас есть функции double(), triple(), square() и cube(), возвращающие некоторое значение. Вы могли бы записать следующую инструкцию:

 

Answer = (double(triple(square(cube(myValue)))));

 

Эта инструкция принимает переменную myValue и передает ее в качестве аргумента функции cube(), возвращаемое значение которой (куб числа) передается в качестве аргумента функции square(). После этого возвращаемое значение функции square() (квадрат числа), в свою очередь, передается в качестве аргумента функции triple(). Затем значение возврата функции triple() (утроенное число) передается как аргумент функции double(). Наконец, значение возврата функции double() (удвоенное число) присваивается переменной Answer.

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

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

 

unsigned long myValue = 2;

unsigned long cubed = cube(myValue); // 2 в кубе = 8

unsigned long squared = square(cubed); // 8 в квадрате = 64

unsigned long tripled = triple(squared); // 64 * 3 = 192

unsigned long Answer = double(tripled); // 192 *2 = 384

 

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

 

 

Параметры - это локальные переменные

 

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

Листинг 5.5. Передача параметров как значений

1: // Листинг 5.5. Передача параметров как значений

2:

3: #include < iostream.h>

4:

5: void swap(int x, int у);

6:

7: int main()

8: {

9: int x = 5, у = 10;

10:

11: cout < < " Main. Before swap, x: " < < x < < " у: " < < у < < " \n";

12: swap(x, y);

13: cout < < " Main. After swap, x: " < < x < < " у: " < < у < < " \n";

14: return 0;

15: }

16:

17: void swap (int x, int у)

18: {

19: int temp;

20:

21: cout < < " Swap. Before swap, x: " < < x < < " у: " < < у < < " \n";

22:

23: temp = x;

24: x = у;

25: у = temp;

26:

27: cout < < " Swap. After swap, x: " < < x < < " у: и < < у < < " \n";

28:

29: }

 

Результат:

Main. Before swap, x: 5 y 10

Swap. Before swap, x: 5 y: 10

Swap. After swap, x: 10 y: 5

Main. After swap, x: 5 y: 10

 

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

Эти переменные инициализируются в строке 9, а отображение их значений на эк­ране выполняется в строке 11. Затем вызывается функция swap(), и эти переменные передаются ей в качестве аргументов.

Выполнение программы переносится в функцию swap(), где в строке 21 снова выводятся значения тех же, уже знакомых нам переменных. Как и ожидалось, их значения от передачи в функцию не изменились. В строках 23—25 переменные ме­няются своими значениями, что подтверждается очередной проверкой в строке 27. Но это положение сохраняется лишь до тех пор, пока программа не вышла из функции swap().

Затем управление программой передается строке 13, принадлежащей функции main(), которая показывает, что переменные получили назад свои исходные значения и все изменения, произошедшие в функции, аннулированы!

Напомним, что в данном случае переменные передаются в функцию swap() как значения, т.е. в функции swap() были созданы копии этих значений, которые являют­ся локальными по отношению к этой функции. Обмен значениями, выполненный в строках 23—25, был реализован на этих локальных переменных, но это никак не по­влияло на переменные, оставшиеся в функции main().

На занятиях 8 и 10 вы узнаете альтернативные способы передачи параметров функциям, которые позволят изменять исходные переменные в функции main().

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2017-03-08; Просмотров: 537; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.034 с.)
Главная | Случайная страница | Обратная связь