Мегаобучалка Главная | О нас | Обратная связь


Передача параметров по ссылке и по значению



2015-11-11 484 Обсуждений (0)
Передача параметров по ссылке и по значению 0.00 из 5.00 0 оценок




Рассмотрим задачу: Известны стороны двух прямоугольников. Нужно вычислить площадь и периметр каждого прямоугольника, а затем напечатать периметр того, чья площадь больше.

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

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

Dim A1, B1, S1, P1 As Integer 'Две стороны, площадь и периметр 1 прямоугольника

Dim A2, B2, S2, P2 As Integer 'Две стороны, площадь и периметр 2 прямоугольника

Dim Площадь As Integer 'площадь, вычисленная процедурой

Dim Периметр As Integer 'периметр, вычисленный процедурой

 

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

'Работаем с 1 прямоугольником:

A1 = 10 : B1 = 50

Прямоугольник(A1, B1)

P1 = Периметр : S1 = Площадь

'Работаем со 2 прямоугольником:

A2 = 20 : B2 = 30

Прямоугольник(A2, B2)

P2 = Периметр : S2 = Площадь

'Анализируем:

If S1 > S2 Then Debug.WriteLine(P1) Else Debug.WriteLine(P2)

End Sub

 

Sub Прямоугольник(ByVal Сторона1 As Integer, ByVal Сторона2 As Integer)

Площадь = Сторона1 * Сторона2

Периметр = 2 * Сторона1 + 2 * Сторона2

End Sub

В учебных целях я здесь написал

A1 = 10 : B1 = 50

Прямоугольник(A1, B1)

хотя короче было бы написать

Прямоугольник(10, 50)

В нашей программе нас явно раздражает необходимость писать строки:

Dim Площадь As Integer 'площадь, вычисленная процедурой

Dim Периметр As Integer 'периметр, вычисленный процедурой

P1 = Периметр : S1 = Площадь

P2 = Периметр : S2 = Площадь

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

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim A1, B1, S1, P1 As Integer 'Две стороны, площадь и периметр 1 прямоугольника

Dim A2, B2, S2, P2 As Integer 'Две стороны, площадь и периметр 2 прямоугольника

A1 = 10 : B1 = 50

Прямоугольник(A1, B1, S1, P1)

A2 = 20 : B2 = 30

Прямоугольник(A2, B2, S2, P2)

If S1 > S2 Then Debug.WriteLine(P1) Else Debug.WriteLine(P2)

End Sub

 

Sub Прямоугольник(ByVal Сторона1 As Integer, ByVal Сторона2 As Integer, _

ByRefПлощадь As Integer, ByRefПериметр As Integer)

Площадь = Сторона1 * Сторона2

Периметр = 2 * Сторона1 + 2 * Сторона2

End Sub

Пояснения. До этого момента мы без пояснений писали в заголовке процедуры перед каждым параметром слово ByVal(по значению). Оно рекомендуется для исходных данных, по которым процедура вычисляет результаты. Ну а вот если процедура эти результаты должна «сообщить внешнему миру», чтобы они использовались где-то вне процедуры (что мы и видим в нашем случае), тогда эти результаты нужно включить в число параметров процедуры и предварить словом ByRef(по ссылке).

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

Прямоугольник(A1, B1, S1, P1)

Лучше всего это делать в пошаговом режиме. К этому моменту в ячейках A1 и B1 уже находятся числа 10 и 50. А в ячейках S1 и P1 пока пусто, точнее – нули.

Но вот желтая полоса прыгает на заголовок процедуры Прямоугольник, управление передалось этой процедуре. В этот славный момент создаются все ее локальные переменные: параметры Сторона1, Сторона2, Площадь, Периметр. И VB требует, чтобы они тут же получили свои значения от вызывающего оператора Прямоугольник (A1, B1, S1, P1). Причем в том порядке, в каком они приведены в скобках. Поэтому Сторона1 получает свое значение от переменной A1, а значит становится равной 10. Сторона2 получает 50 от B1. А Площадь и Периметр получают по нулю от S1 и P1. Кстати, вы можете сказать, что этим двум и не надо было получать никаких нулей, все равно процедура их правильно бы вычислила. Верно, но такая ненужная здесь механика может пригодиться в других программах.

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

Теперь будьте внимательны. Что происходит при вызове и работе процедуры Прямоугольник? В классе, отведенном под процедуру Button1_Click, в этот момент на досках A1, B1, S1, P1 написаны числа: соответственно 10, 50, 0, 0. Для определенности назовем комнатой класс, отведенный под процедуру Прямоугольник. В момент вызова мы видим, что в комнатке на доски, помеченные значком ByVal, автоматически переписываются числа с соответствующих досок класса. То есть, на доску Сторона1 переписывается число 10, а на доску Сторона2 – 50. Но вот странная вещь: вместо досок, помеченных значком ByRef, в комнатке дыры. Вместо доски Площадь – дыра с надписью Площадь, которая ведет в класс прямо к доске S1, и учитель может просунуть в дыру свою длинную руку и писать прямо на доске S1 в классе! Абсолютно то же самое с досками Периметр и P1. Поэтому в рассматриваемый момент с досок S1 и P1 в комнатушку ничего не переписывается. В этом нет нужды, так как учитель все равно имеет полный доступ к этим двум доскам. Через дыры он видит, что там – нули.

Итак, учитель может распоряжаться 4 досками, 2 – в своей комнатке и 2 – в классе. Посмотрим, что происходит, когда учитель выполняет оператор

Площадь = Сторона1 * Сторона2

Он смотрит на доски своей комнатки, видит там 10 и 50, перемножает их, затем просовывает руку в дыру с надписью Площадь и записывает результат 500 на доску S1 в классе. Говорят: переменная Площадь является ссылкойна переменную S1 или что переменная Площадь ссылается на переменную S1.

Мы привыкли, что в ячейках памяти хранятся числа или строки. А что же хранится в «дырявой» ячейке Площадь? Говорят, что в ней хранится ссылка на ячейку S1 или, еще говорят, – хранится адресячейки S1.

Если учитель во время работы что-нибудь напишет на доске со значком ByVal, то бесполезно ждать, что в классе об этом когда-нибудь узнают. Обратного переписывания с досок ByVal в комнатушке на соответствующие доски в классе никогда не происходит! Это значит, что если мы нечаянно пометим параметр Периметр не словом ByRef, а словом ByVal, то все усилия процедуры по вычислению периметра окажутся бесполезными: никто никогда снаружи не узнает вычисленного значения периметра. Проверьте и увидите, что программа в этом случае печатает периметр равный нулю.

Теперь скажем то же самое, но только другими словами: при помощи компьютерной терминологии. Говорят, что ByVal обеспечивает передачу параметров по значению, а ByRef – передачу параметров по ссылке. При передаче параметров по значению процедура работает не с теми ячейками, из которых передается информация (A1, B1), а с собственными ячейками (Сторона1, Сторона2), куда информация из A1, B1 копируется при обращении к процедуре. Процедура может как угодно менять информацию в своих ячейках Сторона1 и Сторона2, на чужих ячейках A1 и B1 это никак не скажется. Поэтому обычно никто из программистов и не старается этого делать.

При передаче параметров по ссылке процедура работает не с собственными ячейками (Площадь, Периметр), а непосредственно с теми ячейками, на которые они ссылаются (S1, P1). Это опасно, ведь процедура получает доступ к локальным переменным другой процедуры, а это не приветствуется, ведь эти переменные становятся беззащитными против ошибок в вызываемой процедуре. Поэтому программисты стараются внимательно следить, чтобы ненароком не записать в чужие переменные что-нибудь не то. И опасное слово ByRef употребляют только тогда, когда хотят передать вызывающей процедуре важные сведения, а в остальных случаях используют безопасное ByVal.

Вот пример, как неоправданное использование ByRef довело нас до беды:

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

Dim A1, B1, S1, P1 As Integer 'Две стороны, площадь и периметр прямоугольника

A1 = 10 : B1 = 50

Прямоугольник_опасный(A1, B1, S1, P1)

Debug.WriteLine(A1 & " " & B1 & " " & S1 & " " & P1)

End Sub

 

Sub Прямоугольник_опасный(ByRef Сторона1 As Integer, ByVal Сторона2 As Integer, _

ByRef Площадь As Integer, ByRef Периметр As Integer)

Площадь = Сторона1 * Сторона2

Периметр = 2 * Сторона1 + 2 * Сторона2

Сторона1 = 999

End Sub

Пояснения. Автор процедуры Прямоугольник_опасный для каких-то своих нужд написал оператор Сторона1 = 999. И все бы ничего, это его право, но он шапкозакидательски написал в заголовке ByRef Сторона1. В результате ни в чем не виноватая процедура Button2_Click вместо того, чтобы напечатать

10 50 500 120

напечатала

999 50 500 120

Достаточно в приведенной программе вместо ByRef Сторона1 снова написать ByVal Сторона1, и все опять будет нормально. Таким образом, передача параметров по значению – еще один способ повысить надежность программирования.

Важное исключение. Слово ByVal теряет свою способность к защите по отношению к массивам и объектам. Ни массивов, ни объектов мы еще не проходили. О причинах невозможности защиты поговорим в 27.2.4.

Далее. В вызывающем операторе на месте параметров, вызываемых по значению, могут стоять не только переменные, но и литералы, и выражения:

Прямоугольник(10, A1+40, S1, P1)

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

А вот на месте параметров, вызываемых по ссылке, могут стоять только переменные! Никаких литералов и выражений!

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



2015-11-11 484 Обсуждений (0)
Передача параметров по ссылке и по значению 0.00 из 5.00 0 оценок









Обсуждение в статье: Передача параметров по ссылке и по значению

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Модели организации как закрытой, открытой, частично открытой системы: Закрытая система имеет жесткие фиксированные границы, ее действия относительно независимы...
Генезис конфликтологии как науки в древней Греции: Для уяснения предыстории конфликтологии существенное значение имеет обращение к античной...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (484)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.006 сек.)