Создание собственных динамических классов
VBA допускает создание динамических структур данных: списков, стеков, очередей, деревьев. Хоть мы и говорим о динамической структуре данных, речь фактически идет о создании динамических типов данных (классов), содержащих как данные, так и операции над ними.
Рассмотрим классический пример и создадим линейный односвязный список. Это можно делать по-разному, но одно из самых разумных решений - определить три класса, задающие
- информационную часть элемента списка;
- структуру элемента списка;
- сам список, в том числе и операции, над ним определенные.
Для определенности рассмотрим список, хранящий информацию о "личностях". Тогда можно считать первую часть нашей задачи решенной,- класс Личность уже создан, и мы его просто используем. Теперь создадим класс с именем ЭлементСпискаЛичностей, задающий структуру элемента (запись). Этот класс прост, поскольку элементы линейного односвязного списка состоят из двух полей - информационного и поля указателя соседнего элемента. Никакие методы над этим классом обычно не определяются. Его полное описание:
'Класс ЭлементСпискаЛичностей содержит только два поля Public Сам As Личность Public Друг As ЭлементСпискаЛичностей
В определении класса ЭлементСпискаЛичностей свойство Друг является объектом того же класса. VBA допускает такую рекурсию в определении - без нее списковую структуру не организовать.
Осталось определить класс СписокЛичностей, задающий сам список. Главное на этом этапе - спроектировать операции над списком. Мы ограничимся набором традиционных методов:
- AddFirst (F As Личность) - добавляет элемент в начало списка;
- AddLast (F As Личность) - добавляет элемент в конец списка;
- Initialize - конструктор по умолчанию, инициализирует список;
- PrintList() - печатает содержимое элементов списка;
- ClearList() - очищает список.
Методы диктуют и состав данных (свойств) класса СписокЛичностей. Нужны минимум два указателя на начало и конец списка: First и Last. Эти свойства разумно закрыть от внешнего использования. Мы добавим к ним открытую переменную Count, следящую за числом элементов списка.
Приведем теперь описание свойств и методов этого класса:
Пример 5.10.
(html, txt)
Теперь несколько замечаний по поводу реализации методов:
- При добавлении нового элемента в методах AddFirst и AddLast вначале создается новый пустой элемент списка; поскольку в объявлении элемента используется спецификатор New. Создается также новый элемент для информационного поля, куда копируется информация, переданная при вызове методов Add, - здесь пригодился метод CopyPerson класса Личность. Заметьте, что в отличие от класса Collection, создается копия элемента, а не используется ссылка. Поэтому состояние элементов списка не зависит от внешних изменений. Изменять содержимое элементов списка можно только с помощью методов списка. Хотя мы и не спроектировали такие методы, понятно, что это нетрудно сделать.
- При печати списка последовательно проходятся все его элементы, для каждого из них вызывается метод PrintPerson, определенный в классе Личность.
- Особо остановимся на методе ClearList, в котором можно установить нулевые значения указателей (Nothing), но нельзя освободить память, занятую элементами списка. Мы специально делаем такую попытку, не приводящую к успеху, чтобы заострить Ваше внимание на этом вопросе. В большинстве языков программирования, позволяющих создавать динамические структуры данных, всегда есть пара взаимосвязанных операций над памятью: "Выделить память" и "Освободить память" (New-Delete или, например, Get-Free). Операции эти выполняются динамически, и выделение и освобождение памяти производится по требованию программиста при выполнении программы. В VBA дело обстоит не так,- здесь есть New, но нет Delete. Связано это, конечно, с особенностями VBA, который, не будучи классическим языком компиляторного типа, представляет нечто среднее между компилятором и интерпретатором. Поэтому здесь освобождение памяти происходит в соответствии с обычными для переменных правилами, определяющими их время жизни. Так что в VBA программист не должен заботиться об освобождении памяти, занятой его динамическими структурами, - это делается автоматически без его участия.
Так не проходит освобождение динамической памяти с помощью метода Unload, выполняющего операцию освобождения памяти, но над объектами другого рода.
Приведем теперь достаточно простой пример, позволяющий построить список в процессе диалога с пользователем и в конце распечатать данные, хранящиеся в построенном списке:
Public Sub ВводСписка() 'Создание списка в диалоге с пользователем Dim Личности As New СписокЛичностей Dim ЭтоЛичность As New Личность Dim Имя As String, Фамилия As String, Дата As Date If MsgBox("Начнем создавать список личностей?", vbYesNo) = vbYes Then Do ЭтоЛичность.ВашеИмя = InputBox("Введите имя личности") ЭтоЛичность.ВашаФамилия = InputBox("Введите Фамилию") ЭтоЛичность.ВашаДатаРождения = InputBox("Введите дату рождения ") Личности.AddLast ЭтоЛичность Loop Until MsgBox("Продолжим создавать список личностей?", vbYesNo) = vbNo Личности.PrintList End If End Sub
Мы не станем приводить реализации других динамических структур, поскольку для тех, кто имеет опыт работы с ними, никаких принципиально новых средств языка не привлекается. Если в языке можно построить список, то можно построить и любую другую динамическую структуру. Но на одном вопросе полезно остановиться. Мы постараемся сейчас показать, как можно объединить достоинства двух подходов: встроенного класса Collection и собственного динамического класса.