Тема 10. Программирование с Использованием объектов и классов

10.3. Создание, уничтожение и операция присваивания объектов

Как всякая динамическая переменная, объект перед началом работы с ним должен быть создан. Нужно выделить под него динамическую область памяти и инициализировать (т.е. подготовить к работе) созданный объект. Для этого в Delphi служит конструктор Create, который является методом класса TObject. Применяется конструктор следующим образом:

10.3. Создание, уничтожение и операция присваивания объектов

Как всякая динамическая переменная, объект перед началом работы с ним должен быть создан. Нужно выделить под него динамическую область памяти и инициализировать (т.е. подготовить к работе) созданный объект. Для этого в Delphi служит конструктор Create, который является методом класса TObject. Применяется конструктор следующим образом:

<Имя-переменной-типа-класс> := <тип-класса> . Create;

После окончания работы с объектом, выделенную под него память необходимо освободить, для чего служат деструкторы класса TObject Destroy или Free. Метод Free удобнее использовать, так как он, в отличие от метода Destroy перед освобождением памяти проверяет, не была ли она уже освобождена ранее, т.е. работает более корректно:

<Имя-переменной-типа-класс> . Free;

При необходимости в состав любого пользовательского класса могут быть введены свои методы Сonstructor и Destructor, которые дополняют Create и Free новыми функциями.

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

10.4. Статический, виртуальный и динамический способы  реализации полиморфизма

При объявлении в разделе Var и последующей работе с несколькими объектами каждый объект располагается по некоторому адресу. Причем все обычные поля копируются, а методы хранятся в одном экземпляре. Каждый раз, когда вызывается метод, ему через параметр-указатель с именем Self, передается адрес того экземпляра объекта, который обращается к методу.

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

Статические методы характеризуются тем, что связывание метода с полями осуществляется во время компиляции (раннее связывание).

Виртуальные и динамические методы связываются во время выполнения программы (позднее связывание). Если метод объявлен виртуальным или динамическим, то нельзя менять типы и число параметров.

Для реализации позднего связывания поступают следующим образом. В потомке замещающий метод объявляется директивой override. Замещаемый одноименный метод родителя объявляется как динамический или виртуальный с помощью ключевых слов (dynamic) или (virtual). Вызов перекрытого метода родительского класса в одноименном методе потомка достигается с помощью зарезервированного слова Inherited (унаследованный).

Встретив объявления dynamic или virtual, компилятор создает таблицы соответствия DMT и VMT. В этих таблицах помещаются адреса точек входа методов. Адрес VMT “своего” класса хранится в каждом экземпляре объекта в особом, скрытом от программиста поле. Адрес DMT хранится в VMT. При каждом обращении к методу компилятор вставляет в соответствующую таблицу код, позволяющий извлечь затем из нее адрес точки входа в подпрограмму.

Отличие таблиц DMT и VMT в том, что DMT содержит адреса только тех методов, которые объявлены как dynamic в данном классе, а VMT содержит адреса всех виртуальных методов данного класса: как нововведенных, так и унаследованных от родителей.

При реализации позднего связывания в родительском классе часто используют абстрактные методы (abstract), т.е. такие виртуальные методы, тело которых не прописано. Классы, имеющие хотя бы один абстрактный метод, сами называются абстрактными. Объекты абстрактных методов не создаются.

10.5. Свойства

Правила ООП требуют, чтобы обращение к полям осуществлялось посредством методов, однако это не всегда удобно. Поэтому класс имеет свойства–специальный механизм, регулирующий доступ к полям. Свойства объявляются с помощью специальной конструкции «property… read… write…;».

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

Это делается, например, следующим образом:

Property x:TPole read GetPole write SetPole;

Метод Getpole служит для чтения, а SetPole – для записи. Если необходимо сделать доступ к полю только для чтения, то следует опустить write.

10.6. Пример написания программы

Задание: Составить родительский класс, который позволяет с помощью методов Show и Hide показывать и стирать объекты на экране. Написать классы-потомоки, который рисуют круг, квадрат, комбинацию круга и квадрата (человечек). Предусмотреть возожность перемещения объектов и изменения их размера.

Панель диалога представлена на рис. 10.1.

Рис. 10.1.

Текст модуля приведен на Листинге 10.1, а текст программы на Листинге 10.2.

Листинг 10.1.

Unit Unit2;

Interface

uses Graphics;

var  ColrBack:TСolor;

Type

  Tviz=class(Tobject)  // Абстрактный родительский класс

  ColrLine : Tcolor; 

  Canvas : Tcanvas;

  x, y, r : word;

  Procedure Ris;virtual;abstract;  // Перекрываемый метод для рисования

  Procedure Draw(bl:boolean); 

  procedure Show;  // Показать изображение

  procedure Hide;  // Стереть изображение

  procedure MovTo(dx,dy,dr:integer);  // Сдвинуть и изменить размер

  end;

  TKrug=class(Tviz)  // Класс рисования круга

  x1,y1,x2,y2:word;

  Constructor Create(x0,y0,r0:word; colrLine0:Tcolor;canvas0:Tcanvas);

  Procedure Ris; override;

  end;

  TKvad=class(Tkrug)  // Класс рисования квадрата

  Procedure Ris; override;

  end;

  TKrPr=class(Tkrug)  // Класс рисования круга на квадрате

  dy1:word;

  Constructor Create(x0,y0,r0,dy0:word; colrLine0:Tcolor;canvas0:Tcanvas);

  Procedure Ris; override;

  end;

Implementation  //Ниже описываются тела всех методов

  Procedure Tviz.Draw;        // В зависимости от значения булевой

  begin                                // переменной этот метод рисует картинки Ris

  with Canvas do begin        // либо цветом линии, либо цветом фона.

  if bl then begin                // В последнем случае происходит стирание

  pen.color:=colrLine;         brush.color:=colrLine

  end

  else begin

  pen.color:=colrBack;  brush.color:=colrBack

  end;

                  Ris;                  // Процедура ris что-то рисует

  end;  end;

  Procedure Tviz.Show;

  begin

  Draw(true);

  end;

Procedure Tviz.Hide;

begin

  Draw(false); 

end;

procedure Tviz.MovTo;

begin

  Hide;

  x:=x+dx;  y:=y+dy;  r:=r+dr;  // Переход к новым координатам

  Show;

end;

  Constructor TKrug.Create;        // Начальные данные для рисования круга

  begin                                 // они такие-же как и для рисования квадрата,

  colrLine:=colrLine0;                        // поэтому класс Tkvad наследует его

  canvas:=canvas0;

  x:=x0; y:=y0; r:=r0;

  end;

  Procedure Tkrug.Ris;                // Рисование круга

  Begin

       x1:=x-r;  x2:=x+r;  y1:=y-r;  y2:=y+r;

  Canvas.Ellipse(x1,y1,x2,y2);

  end;

  Procedure Tkvad.ris;                // Рисование квадрата

Begin

       x1:=x-r;  x2:=x+r;  y1:=y-r;  y2:=y+r;

  Canvas.Rectangle(x1,y1,x2,y2);

  end;

  Constructor TKrpr.Create;        // Начальные данные для рисования круга

  Begin                                        // на квадрате

  dy1:=dy0;

  Inherited Create(x0,y0,r0,colrLine0,canvas0); // Используется метод TKrug

  end;

  Procedure TkrPr.Ris;        // Рисование квадрата под кругом

  begin

  Inherited ris // Используется родительский метод рисования круга

  Canvas.Rectangle(x1,y2,x2,y2+dy1);

  end;

end.

Листинг 10.2.

unit Unit10;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls, Buttons, ExtCtrls;

type

  TForm1 = class(TForm)

  Button1: TButton;

  Button2: TButton;

  Button3: TButton;

  Button4: TButton;

  Button5: TButton;

  Button6: TButton;

  Button7: TButton;

  Button8: TButton;

  BitBtn1: TBitBtn;

  Image1: TImage;

  procedure Button1Click(Sender: TObject);

  procedure Button2Click(Sender: TObject);

  procedure Button3Click(Sender: TObject);

  procedure Button4Click(Sender: TObject);

  procedure Button5Click(Sender: TObject);

  procedure Button6Click(Sender: TObject);

  procedure Button7Click(Sender: TObject);

  procedure Button8Click(Sender: TObject);

  procedure BitBtn1Click(Sender: TObject);

  private   { Private declarations }

public   { Public declarations }

  end;

var    Form1: TForm1;

implementation

{$R *.dfm}

uses unit2, Clipbrd;

var krug:Tkrug;

  kvad:Tkvad;

  krpr:Tkrpr;

  okno1:Timage; // Введена переменная для сохранения записи

  pxm1,pym1, xo,yo,ro:word;

procedure TForm1.Button1Click(Sender: TObject);  // Нарисовать

begin

okno1:=Form1.Image1;

colrBack:=clWhite;                 // Цвет фона – белый

pxm1:=okno1.ClientWidth;         // Считывание размеров окна

pym1:=okno1.ClientHeight;

with okno1.canvas do begin

pen.color:=colrBack;

brush.color:=colrBack;

Rectangle(0,0,Pxm1,Pym1);         // Заливка всего окна цветом фона

  end;

xo:=pxm1 div 2;  yo:=pym1 div 2;        // Вычисление координат центра окна

ro:=10;                                // Начальный размер круга и прямоугольника

Krug:=Tkrug.Create(xo,yo,ro,clBlack,okno1.canvas);        // Цвет – черный

Kvad:=Tkvad.Create(xo+80,yo,ro,clBlack,okno1.canvas);        

Krpr:=Tkrpr.Create(xo-80,yo,ro,2*ro,clBlack,okno1.canvas);

krug.Show;        // Рисование круга

kvad.Show;        // Рисование квадрата

Krpr.show;        // Рисование комбинации круга и квадрата

end;

procedure TForm1.Button2Click(Sender: TObject);

begin                        // Увеличить круг

Krug.MovTo(0,0,3);

end;

procedure TForm1.Button3Click(Sender: TObject);

begin                        // Двигать квадрат вправо-вниз

Kvad.MovTo(3,3,0);

end;

procedure TForm1.Button4Click(Sender: TObject);

begin                        // Ход конем вправо-вниз

krpr.MovTo(10,0,0); 

okno1.Update;                // Прорисовка окна

sleep(200);                // Задержка

krpr.MovTo(0,5,0);

end;

procedure TForm1.Button5Click(Sender: TObject);

begin                        // Уменьшить

Krug.MovTo(0,0,-3);

end;

procedure TForm1.Button6Click(Sender: TObject);

begin                        // Двигать влево-вверх

Kvad.MovTo(-3,-3,0);

end;

procedure TForm1.Button7Click(Sender: TObject);

begin                        // Ход конем влево-вверх

krpr.MovTo(-10,0,0);

okno1.Update; 

  sleep(200);

krpr.MovTo(0,-5,0);

end;

procedure TForm1.Button8Click(Sender: TObject);

begin                // Только для TImage !!! сохранить картинку

Clipboard.Assign(Image1.Picture);

end;

procedure TForm1.BitBtn1Click(Sender: TObject);

begin

krug.Free;  kvad.Free;  Krpr.Free;

end;

end.

10.7. Выполнение индивидуального задания

По указанию преподавателя выберите вариант задачи.

Описать класс-родитель и класс-потомок, имеющие методы, указанные в соответствующем варианте задания (потомок наследует или переопределяет методы родителя и приобретает новые). Предусмотреть необходимое количество кнопок для демонстрации каждого из методов объектов.

1. Нарисовать вращающееся колесо. Родительский класс – перемещающийся круг.

2. Нарисовать повозку (прямоугольник на 2 колесах). Родительский класс – перемещающийся прямоугольник.

3. Нарисовать ракету с пламенем из сопла. Родительский класс – перемещающийся отрезок.

4. Нарисовать рожицу двигающую глазами и открывающаю рот. Родительский класс – перемещающийся элипс.

5. Нарисовать солдатика, перемещающегося и отдающего честь. Родительский класс – перемещающийся прямоугольник.

6. Нарисовать кораблик, который может поднимать флаг. Родительский класс – перемещающийся прямоугольник.

7. Нарисовать автомобиль с открывающимися дверями и включающимися фарами. Родительский класс – перемещающийся прямоугольник.

8. Нарисовать сигнальщика, подающего различные сигналы. Родительский класс – перемещающийся прямоугольник.

9. Нарисовать самосвал, который может поднимать кузов. Родительский класс – перемещающийся прямоугольник.

10. Нарисовать самолет, который может при посадке выпускает шасси. Родительский класс – перемещающийся прямоугольник.

11. Нарисовать домик, в котором открываются двери и окна. Родительский класс – перемещающийся прямоугольник.

12. Нарисовать паровоз, который выпускает дым. Родительский класс – перемещающийся прямоугольник.

13. Нарисовать воздушний шарик, который может лопнуть. Родительский класс – перемещающийся эллипс

14. Нарисовать лифт, который доставляет людей на нужный этаж. Родительский класс – перемещающийся прямоугольник.

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

Вы здесь: