Библиотека программиста

«Программирование, как и любовь - это одно слово, за которым скрывается бесчисленное множество занятий»

Главная страница > Язык Object Pascal > 22. Введение в классы и объекты

22. Введение в классы и объекты

Класс как тип представляет собой структуру, состоящую из полей, методов и свойств (fields, methods и properties). Экземпляры (instances) типа класс называются объектами (objects). Поля, методы и свойства называются его компонентами или членами (components или members).

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

Метод – это процедура или функция, связанная (ассоциированная) с классом. Большинство методов оперируют над объектами, т.е. экземплярами классов. Некоторые методы, называемые методами классов (class methods), оперируют над классами как типами.

Свойства (properties) представляют собой интерфейс к данным объекта, которые (данные) обычно хранятся как поля.

Свойства имеют спецификаторы доступа (access specifiers), которые определяют, как данные читаются или модифицируются. Для внешних программ, т.е. программ, которые используют объект, свойства выглядят просто как поля данных, значения которых можно получать или изменять.

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

Переменная типа класс в действительности представляет собой указатель, который указывает на объект. Следовательно, более чем одна такая переменная может ссылаться на один и тот же объект. Также подобно другим указателям, переменная типа класс может иметь значение nil. Однако программист не должен разыменовывать (dereference) явно переменную типа класс для того, чтобы получить доступ к объекту. Например, SomeObject.Size := 100 присвоит значение 100 свойству объекта SomeObject и нельзя писать SomeObject^.Size := 100.

Описание класса.

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

type className = class (ancestorClass)

memberList

end;

Имя родительского класса ancestorClass может быть опущено, и в этом случае новый класс выступает в качестве потомка предопределенного класса Tobject. Если имя родительского класса указано, а список компонентов класса memberList является пустым, можно опустить end. Описание класса может также включать список интерфейсов.

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

Например, вот описание класса TListColumns из модуля ComCtrls библиотеки визуальных компонентов (Delphi’s VCL).

type

TListColumns = class(TCollection)

private //

FOwner: TCustomListView;

function GetItem(Index: Integer): TListColumn;

procedure SetItem(Index: Integer; Value: TListColumn);

protected//

function GetOwner: TPersistent; override;

procedure Update(Item: TCollectionItem); override;

public//

constructor Create(AOwner: TCustomListView);

function Add: TListColumn;

property Owner: TCustomListView read FOwner;

property Items[Index: Integer]: TListColumn read GetItem write SetItem; default;

end;

Этот класс наследуется от класса TCollection из модуля Classes, однако он определяет или переопределяет несколько методов и свойств, включая конструктор Create. Деструктор класса Destroy наследуется без изменений и поэтому не переопределяется. Каждый компонент класса объявлен как private, protected или public, а компонентов published этот класс не содержит.

После такого описания (разумеется, и методов тоже) можно описать объект

var ListColumns: TListColumns;

и создать его

ListColumns := TListColumns.Create(SomeListView);

где SomeListView – переменная, связанная с объектом типа TCustomListView.

Наследование.

При объявлении класса можно указать его непосредственного родителя (ancestor), например

type TSomeControl = class(TWinControl);

описывает класс TSomeControl, который наследуется от TWinControl. Новый класс наследует все компоненты родительского класса, может иметь новые компоненты и может переопределить унаследованные, но не может удалить компоненты, имеющиеся у родительского класса. Таким образом, класс TSomeControl содержит все компоненты класса TWinControl и всех классов, наследником которых является TWinControl.

Область видимости описания компонента начинается с точки, в которой он описан, и простирается до конца описания класса. Она распространяется также на все методы и на всех наследников (descendants) данного класса.

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

Типы TObject и TClass.

Класс TObject, объявленный в модуле System, является прародителем для всех других классов. Он содержит описания только методов, включая конструктор и деструктор. В дополнение к TObject модуль System включает описание типа "ссылка на класс" (class-reference type)

TClass = class of TObject;

Если даже описание класса не определяет родительский класс, описываемый класс считается производным (derived) от класса TObject. Таким образом, описание

type TMyClass = class

. . .

end;

эквивалентно

type TMyClass = class(TObject)

end;

Последняя форма рекомендуется для повышения читабельности.

Типы TObject и TClass в модуле System описаны так:

type

TObject = class;

TClass = class of TObject;

TObject = class

constructor Create;

destructor Destroy; virtual;

class function ClassInfo: Pointer;

class function ClassName: ShortString;

class function ClassNameIs(const Name: string): Boolean;

class function ClassParent: TClass;

function ClassType: TClass;

procedure CleanupInstance;

procedure DefaultHandler(var Message); virtual;

procedure Dispatch(var Message);

function FieldAddress(const Name: ShortString): Pointer;

procedure Free;

procedure FreeInstance; virtual;

class function InheritsFrom(AClass: TClass): Boolean;

class function InitInstance(Instance: Pointer): TObject;

class function InstanceSize: Longint;

class function NewInstance: TObject; virtual;

class function MethodAddress(const Name: ShortString): Pointer;

class function MethodName(Address: Pointer): ShortString;

end;

?????? Остановиться подробно на отличиях значений типа TObject и TClass, возвращаемых функциями. Пояснить, зачем они нужны. Вот пример программы:

Program DemoTClass;

{иллюстрация методов ClassName, ClassType и операции is

@Овсянник В.Н. октябрь 2003}

{$APPTYPE CONSOLE}

uses SysUtils,Windows;

{WinToDos - преобразует символы из кодировки Windows в MS DOS для использования

в консольных приложениях. То же самое делает функция

Windows.CharToOem}

Function WinToDos(const s : string):string;

var

i,code : byte;

Begin

SetLength(Result,Length(s));

{в следующем цикле изменяем только коды русских букв,

не изменяя кодов остальных символов}

for i:=1 to Length(s) do

begin

code:=Ord(s[i]);

case code of

192..239 : Result[i]:=Chr(code-64);//А..Я,а..п

240..255 : Result[i]:=Chr(code-16);//р..я

168 : Result[i]:=#240; // Ё

184 : Result[i]:=#241; // ё

{украинские буквы}

178 : Result[i]:=#73; // І

179 : Result[i]:=#105; // і

170 : Result[i]:=#242; // Є

186 : Result[i]:=#243; // є

175 : Result[i]:=#244; // Ї

191 : Result[i]:=#245; // ї

{все остальные символы}

else Result[i]:=s[i];

end;

end;

End;

type

TMyClass = class

end;

var

a : TMyClass;

Res : array [0..120] of char;

BEGIN

CharToOem('Имя класса=',Res);

a:=TMyClass.Create;

WriteLn(WinToDOS('Имя класса='),a.ClassName);

WriteLn(Res,a.ClassName);

if a.ClassType=TMyClass then WriteLn(WinToDOS('a - имеет тип TMyClass'));

if a is TObject then

WriteLn(WinToDOS('a - имеет тип TObject или производный от него'));

ReadLn;

END.

Совместимость типов класс (Compatibility of class types).

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

type

TFigure = class(TObject);

TRectangle = class(TFigure);

TSquare = class(TRectangle);

var

Fig: TFigure;

переменной Fig может быть присвоено значение любого из типов TFigure, TRectangle и TSquare.

Объектные типы (object types).

Как альтернативу типу класс можно объявить объектный тип с помощью описания

type objectTypeName = object (ancestorObjectType)

memberList

end;

Если предок не указан, то новый тип не имеет родителей. Объектные типы не могут иметь опубликованных компонент (published members). Так как у такого объектного типа нет родителей, у него нет и никаких методов, в том числе конструктора и деструктора. Объектный тип оставлен только для обратной совместимости и его не рекомендуется использовать.





<< Предыдущая статья
«21. Вызовы подпрограмм»
Следующая статья >>
23. Видимость компонентов класса