Я завершила экзамен 90 баллов на 5. Сертификат не заказала. Сейчас пытаюсь найти как его заказать. у меня указано экзамен пройден баллы оценка видно, а чтоб заказать сертификат нигде не видно. |
Иерархии объектов. Работа с объектами в динамической памяти
Объекты в динамической памяти
Для хранения объектов в программах чаще всего используется динамическая память, поскольку это обеспечивает гибкость программы и эффективное использование памяти. Благодаря расширенной совместимости типов можно описать указатель на базовый класс и хранить в нем ссылку на любой его объект-потомок, что в сочетании с виртуальными методами позволяет единообразно работать с различными классами иерархии. Из объектов или указателей на объекты создают различные динамические структуры.
Для выделения памяти под объекты используются процедура и функция new. Например, если определены указатели:
type pmonster = ^monster; pdaemon = ^daemon; var pm : pmonster; pd : pdaemon;
можно создать объекты с помощью вызовов:
new(pm); { или pm := new(pmonster); } new(pd); { или pd := new(pdaemon); }
При использовании new в форме процедуры параметром является указатель, а в функцию передается его тип. Так как после выделения памяти объект обычно инициализируют, для удобства определены расширенные формы new с двумя параметрами. На месте второго параметра задается вызов конструктора объекта.
new(pm, init(1, 1, 1, 1); { или pm := new(pmonster, init(1, 1, 1, 1)); } new(pd, init(1, 1, 1, 1, 1); { или pd := new(pdaemon, init(1, 1, 1, 1, 1)); }
Обращение к методам динамического объекта выполняется по обычным правилам Паскаля, например:
pm^.draw; pm^.attack;
С объектами в динамической памяти часто работают через указатели на базовый класс, то есть описывают указатель базового класса, а инициализируют его, создав объект производного класса, например:
pm := new(pdaemon, init(1, 1, 1, 1, 1));
Как уже говорилось, такие объекты называют полиморфными. Они используются для того, чтобы можно было единообразно работать в программе с объектами разных классов. Например, оператор pm^.draw будет автоматически вызывать разные методы в зависимости от того, на объект какого типа в данный момент ссылается указатель pm (это справедливо только для виртуальных методов).
Для освобождения памяти, занятой объектом, применяется процедура Dispose:
Dispose(pm);
При выполнении этой процедуры освобождается количество байтов, равное размеру объекта, соответствующего типу указателя. Следовательно, если на самом деле в указателе хранится ссылка на объект производного класса, который, как известно, может быть больше своего предка, часть памяти не будет помечена как свободная, но доступ к ней будет невозможен, то есть появится мусор ( рис. 7.3). Второй случай появления мусора возникает при применении процедуры Dispose к объекту, поля которого являются указателями ( рис. 7.4). Объект, содержащий динамические поля, мы рассмотрим в конце этой лекции.
Для корректного освобождения памяти из-под полиморфных объектов следует использовать вместе с процедурой Dispose специальный метод — деструктор. В документации по Borland Pascal ему рекомендуется давать имя done, например:
destructor monster.done; begin end;
Для правильного освобождения памяти деструктор записывается вторым параметром процедуры Dispose.
Dispose(pm, done);
Для простых объектов деструктор может быть пустым, а для объектов, содержащих динамические поля, в нем записываются операторы освобождения памяти для этих полей. В деструкторе можно описывать любые действия, необходимые для конкретного объекта, например закрытие файлов. Исполняемый код деструктора никогда не бывает пустым, потому что компилятор по служебному слову destructor вставляет в конец тела метода операторы получения размера объекта из VMT. Деструктор передает этот размер процедуре Dispose, и она освобождает количество памяти, соответствующее фактическому типу объекта.
Деструкторы рекомендуется делать виртуальными, для того чтобы при вызове всегда выполнялся деструктор, соответствующий типу объекта. Деструкторы обязательно использовать только для динамических полиморфных объектов, однако можно их применять и для статических объектов. В объекте можно определить несколько деструкторов (естественно, в этом случае они должны иметь разные имена).
Вариант кода модуля monsters, содержащий конструкторы и деструкторы (он используется в следующем разделе), приведен в пример 7.3.
unit monsters; interface uses Graph; type pmonster = ^monster; monster = object constructor init(x_, y_, health_, ammo_ : word); procedure attack; virtual; procedure draw; virtual; procedure erase; virtual; procedure hit; procedure move(x_, y_ : word); destructor done; private x, y : word; health, ammo : word; color : word; end; pdaemon = ^daemon; daemon = object (monster) constructor init(x_, y_, health_, ammo_, magic_ : word); procedure attack; virtual; procedure draw; virtual; procedure erase; virtual; procedure wizardry; private magic : word; end; implementation { ------------------- реализация методов объекта monster ---------------------- } constructor monster.init(x_, y_, health_, ammo_ : word); begin x := x_; y := y_; health := health_; ammo := ammo_; color := yellow; end; procedure monster.attack; { -------------------------------- monster.attack --- } begin if ammo = 0 then exit; dec(ammo); setcolor(color); outtextXY(x + 15, y, 'ба-бах!'); end; procedure monster.draw; { -------------------------------- monster.draw ----- } begin setcolor(color); outtextXY(x, y, '@'); end; procedure monster.erase; { ---------------------------------- monster.erase --- } begin setcolor(black); outtextXY(x, y, '@'); end; procedure monster.hit; { ---------------------------------- monster.hit ----- } begin if health = 0 then exit; dec(health); if health = 0 then begin color := red; draw; exit; end; attack; end; procedure monster.move(x_, y_ : word); { --------------------- monster.move --- } begin if health = 0 then exit; erase; x := x_; y := y_; draw; end; destructor monster.done; { ----------------------------------- monster.done --- } begin end; { ----------------------- реализация методов объекта daemon ------------------- } constructor daemon.init(x_, y_, health_, ammo_, magic_ : word); begin inherited init(x_, y_, health_, ammo_); color := green; magic := magic_; end; procedure daemon.draw; { ----------------------------------- daemon.draw ---- } begin setcolor(color); outtextXY(x, y, '%)'); end; procedure daemon.erase; { ----------------------------------- daemon.erase --- } begin setcolor(black); outtextXY(x, y, '%)'); end; procedure daemon.attack; { ---------------------------------- daemon.attack --- } begin if ammo = 0 then exit; dec(ammo); if magic > 0 then begin outtextXY(x + 15, y, 'БУ-БУХ!'); dec(magic); end else outtextXY(x + 15, y, 'бу-бух!'); end; procedure daemon.wizardry; { -------------------------------- daemon.wizardry - } begin if magic = 0 then exit; outtextXY(x + 15, y, 'крибле-крабле-бумс!'); dec(magic); end; end.Листинг 7.3. Модуль monsters, использующий конструкторы и деструкторы
Для использования этого модуля не обязательно иметь в распоряжении его полный исходный код — достаточно интерфейсного раздела (и, конечно, файла .tpu ). В программе, использующей этот модуль, можно описывать производные классы, в которых добавлены новые поля и методы и переопределены имеющиеся. При этом новые объекты будут перемещаться с помощью метода, который был написан до их появления!