ПОНЯТНО О Visual Basic NET (том 3)

       

Механизм действия ссылочных типов


Рассмотрим три похожих фрагмента:

Фрагмент 1:

Фрагмент 2:

Фрагмент 3:

Dim A As Integer

Dim B As Integer



A = 5

B = A

Debug.WriteLine(B)

A = 100

Debug.WriteLine(B)

Dim A(2) As Integer

Dim B(2) As Integer

A(2) = 5

B = A

Debug.WriteLine(B(2))

A(2) = 100

Debug.WriteLine(B(2))

Dim A As New Класс

Dim B As New Класс

A.Поле= 5

B = A

Debug.WriteLine(B.Поле)

A.Поле= 100

Debug.WriteLine(B.Поле)

Что будет напечатано:

Что будет напечатано:

Что будет напечатано:

5

5

5

100

5

100

Вот код класса для третьего фрагмента:

Class Класс

    Public Поле As Integer

    Public Поле1 As Integer

End Class

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

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

При выполнении оператора   Dim A, B As Integer   переменным A и B отводится в памяти по ячейке (которые заполняются нулями). При выполнении оператора  A = 5  в ячейке A появляется пятерка:

Адреса

Значения

A

000101

5

 

B

000102

0

При выполнении оператора  B = A  пятерка из ячейки A копируется в ячейку B, а то, что впоследствии пятерка в ячейке A меняется на сотню, никак не влияет на содержимое ячейки B.

Все происходит привычно, потому что тип Integer, к которому принадлежат переменные A и B, относится к обычным типам.

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


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

2. Массивы. Рассмотрим второй фрагмент. Вот что находится в памяти после выполнения оператора A(2) = 5:

Адреса

Значения

Адреса

Значения

A

000101

002040



002040

0

002041

0

002042

5

B

000102

002300



002300

0

002301

0

002302

0

На схеме я для наглядности изобразил ячейки в два столбца.

Пояснения: При выполнении операторов

Dim A(2) As Integer

Dim B(2) As Integer

каждой из переменных A и B отводится в памяти одна

ячейка и еще дополнительная область памяти для хранения  элементов массива (по три ячейки, которые заполняются нулями). В этой одной ячейке находится адрес первой ячейки из области памяти или, по-другому, ссылка на эту область памяти. Ссылку еще называют указателем. Вот такой механизм. Таким образом, значением переменной A является совсем не набор из трех чисел, а всего лишь адрес, номер ячейки. Запомните это.

Когда выполняется оператор A(2) = 5,  компьютер, зная, что массив является ссылочным типом, рассматривает значение 002040 в ячейке для переменной A именно, как адрес, а не число типа Integer, и поэтому не пытается записать в эту ячейку число 5, а  отправляется по указанному адресу, где находит три ячейки. В какую из них записать число 5, ему указывает индекс 2.

Рассмотрим, как выполняется оператор B = A.  Вам могло показаться по аналогии с первым фрагментом, что при этом все три числа из области памяти для A копируются в область памяти для B.  Но нет, здесь по своему строгому, но привычному вам закону выполняется оператор присваивания. Закон этот говорит, что значение переменной A должно быть записано в ячейку для значения переменной B. Поскольку значением переменной A является адрес 002040, то он и копируется в ячейку для значения переменной B. Вот что получается:

Адреса

Значения

Адреса

Значения

A

000101

002040



002040

0

002041

0

002042

5

B

000102

002040

002300

0

002301

0

002302

0

<


Вы видите, что три числа в области памяти 002300 не изменились. Почему же тогда первый из двух операторов  Debug.WriteLine(B(2))  печатает пятерку, а не ноль? Вот почему. Механизм обращения к памяти здесь тот же, что и описанный парой абзацев ранее механизм обращения к памяти при выполнении оператора A(2) = 5. Компьютер, зная, что массив является ссылочным типом, рассматривает значение 002040 в ячейке для переменной B, как адрес, и отправляется по указанному адресу, где находит три ячейки. Индекс 2 указывает ему, что для печати нужно выбрать значение из последней ячейки, а там находится пятерка.

А кому же теперь нужна область 002300? В том-то и дело, что никому! Ссылок на нее теперь не существует и воспользоваться ей уже нельзя, даже если вы и захотите. Она превратилась в мусор (garbage). Уборкой мусора занимается VB и делает это без вашего ведома и участия.

Я думаю, что теперь мне не нужно объяснять, почему второй из двух операторов  Debug.WriteLine(B(2))  напечатает сотню.

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

Адреса

Значения

Адреса

Значения

A

000101

002040



Поле

002040

100

Поле1

002041

0

B

000102

002040

Поле

002300

0

Поле1

002301

0

Задание 21.         

Что напечатает фрагмент:

        Dim A, B, C As Класс

        A = New Класс

        A.Поле = 100

        B = A

        C = New Класс

        C.Поле = 50

        A = C

        Debug.WriteLine(A.Поле)

        Debug.WriteLine(B.Поле)


Содержание раздела