Опубликован: 03.09.2010 | Доступ: свободный | Студентов: 3417 / 749 | Оценка: 4.11 / 3.69 | Длительность: 10:38:00
Лекция 7:

Иерархии объектов. Работа с объектами в динамической памяти

< Лекция 6 || Лекция 7: 12345

Объекты в динамической памяти

Для хранения объектов в программах чаще всего используется динамическая память, поскольку это обеспечивает гибкость программы и эффективное использование памяти. Благодаря расширенной совместимости типов можно описать указатель на базовый класс и хранить в нем ссылку на любой его объект-потомок, что в сочетании с виртуальными методами позволяет единообразно работать с различными классами иерархии. Из объектов или указателей на объекты создают различные динамические структуры.

Для выделения памяти под объекты используются процедура и функция 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). Объект, содержащий динамические поля, мы рассмотрим в конце этой лекции.

 Неверное удаление полиморфного объекта

Рис. 7.3. Неверное удаление полиморфного объекта

 Неверное удаление объекта с динамическими полями

Рис. 7.4. Неверное удаление объекта с динамическими полями

Для корректного освобождения памяти из-под полиморфных объектов следует использовать вместе с процедурой Dispose специальный метод — деструктор. В документации по Borland Pascal ему рекомендуется давать имя done, например:

destructor monster.done;
begin
end;

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

Dispose(pm, done);

Для простых объектов деструктор может быть пустым, а для объектов, содержащих динамические поля, в нем записываются операторы освобождения памяти для этих полей. В деструкторе можно описывать любые действия, необходимые для конкретного объекта, например закрытие файлов. Исполняемый код деструктора никогда не бывает пустым, потому что компилятор по служебному слову destructor вставляет в конец тела метода операторы получения размера объекта из VMT. Деструктор передает этот размер процедуре Dispose, и она освобождает количество памяти, соответствующее фактическому типу объекта.

ВНИМАНИЕ Вызов деструктора вне процедуры 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 ). В программе, использующей этот модуль, можно описывать производные классы, в которых добавлены новые поля и методы и переопределены имеющиеся. При этом новые объекты будут перемещаться с помощью метода, который был написан до их появления!

< Лекция 6 || Лекция 7: 12345
София Шишова
София Шишова

Я завершила экзамен 90 баллов на 5. Сертификат не заказала. Сейчас пытаюсь найти как его заказать. у меня указано экзамен пройден баллы оценка видно, а чтоб заказать сертификат нигде не видно.