Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Указатель Self




Методы

 

Методы – это процедуры и функции, которые реализуют правила решения задачи, инкапсулируемой данным классом. Методы нельзя использовать вне класса. Обратиться к тому или иному методу можно лишь только с указанием экземпляра класса соответствующего типа. В рамках определения класса любой метод имеет тот или иной уровень видимости – public, protected, или private. Из прикладной программы «видимы» только public – методы. При проектировании класса вопрос о том, в какую секцию поместить тот или иной метод, решает разработчик. Вообще говоря, этот вопрос нельзя отнести к категории тривиальных. Отметим общие соображения по этому поводу.

Public – методы и свойства образуют пользовательский интерфейс класса. Только через public – методы и свойства пользователю доступны функциональные возможности класса. Допустим, что нам надо спроектировать класс, инкапсулирующий задачу записи и воспроизведения звуковых файлов. Интерфейс такого класса мог бы включать такие методы как Open, Play, Record, Save, Rewind и т.п.

Private – методы и свойства – это «кухня» класса, а иначе то, о чем пользователь знать не должен. Очень часто возникает необходимость выполнения большой подготовительной работы при создании экземпляра класса. Чтобы сделать конструктор более компактным, часть подготовительной работы можно вынести в отдельный метод, например Init, и обратиться к нему из контсруктора. Однако, самостоятельного значения такой метод, как правило, не имеет. Более того, его вызов непосредственно из программы может быть просто опасным. Чтобы предотвратить возможные нарушения целостности класса подобные методы и делают скрытыми.

Protected – методы и свойства не доступны в прикладной программе. В этом они сходны с private. Однако, при проектировании новых классов на основе существующих (т.е. при наследовании) такие методы доступны разработчику. Механизм наследования рассматривается ниже.

В Object Pascal существует понятие «классовый метод». В отличие от метода класса, классовый метод – это обычная подпрограмма, объявление которой включено в объявление данного класса. Классовые методы можно рассматривать как те процедуры и функции, которые описывают действия над экземплярами данного и (или) родственных классов, а не над полями данного класса. Так, ни поля, ни методы класса не доступны в классовом методе. Если например, мы создаем класс «матрица», то процедуры действий над матрицами – сложение, умножение и т.п. целесообразно объявить классовыми методами, ибо они выполняют операции над цельными объектами, а не над их частями. Для обращения к классовому методу в программе необходимо указать вместо имени экземпляра (объекта) указать имя класса. Отметим, что аналогичный синтаксис используется для вызова конструктора.

 


 

 

Новый термин. Все классы имеют скрытое поле с именем Self. Self – это указатель на «свой» экземпляр в памяти.

 

Поясним смысл Self на примере, показывающем как бы выглядело объявление класса TMyRect, если поле Self объявить явно:

 

TMyRect = class

private

Self: TMyRect;

Left: Integer;

Top: Integer;

Right: Integer;

Bottom: Integer;

Text: PChar;

public

function GetWidth: Integer;

function GetHeight: Integer;

procedure SetRect(ALeft, ATop, ARight, ABottom: Integer);

constructor Create;

constructor CreateVal(ALeft,ATop,ARight,ABottom: Integer);

destructor Destroy; override;

end;

 

Точно так компилятор и «видит» объявление любого класса. На самом деле компилятор сам добавляет поле Self к любому объекту. При создании объекта указатель Self инициализируется автоматически – в него заносится адрес структуры данных класса:

 

Rect:= TMyRect.CreateVal(0, 0, 100, 100);

{ Теперь Rect и Rect.Self имеют одинаковые значения }

{ поскольку содержат один и тот же адрес памяти. }

 

Зачем же нам может понадобится поле Self? Вспомним, что каждый объект имеет собственную копию (экземпляр) полей в памяти. В то же время, все объекты одного и того же класса используют один и тот же исполняемый код методов. Иными словами, несколько экземпляров данных разделяют одни и те же методы. Компилятор всегда использует поле Self чтобы однозначно передать в метод те данные (т.е. – поля), которые принадлежат конкретному экземпляру класса – объекту. Рассмотрим, например такую версию описания метода GetWidth:

 

 

function TMyRect.GetWidth: Integer;

begin

Result:= Right - Left;

end;

 

Именно так ее «видит» программист. Компилятор, однако интерпретирует этот текст по–своему, а именно:

 

function TMyRect.GetWidth: Integer;

begin

Result:= Self.Right - Self.Left;

end;

 

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

 

ПРЕДУПРЕЖДЕНИЕ: Никогда не изменяйте значение поля Self. Его можно использовать как указатель на объект класса в памяти или передавать в качестве фактического параметра другому объекту. Случайное (преднамеренное) изменение поля Self может привести к непредсказуемым последствиям. Это поле предназначено только для чтения.

 

Хотя Self – это «боец невидимого фронта», его можно эффективно использовать в программах. Например, рассмотрим как можно поместить на форму компонент во время выполнения программы, а не во время разработки. Этот пример иллюстрирует наиболее типичный случай использования указателя Self в программах Delphi.

Прежде всего отметим, что когда вы перетаскиваете компонент с палитры на форму, Delphi создает указатель на этот компонент и выполняет определенную работу, скрывая от программиста все ее детали. Если необходимо поместить копмонент на форму во время выполнения программы, руководство по VCL настоятельно требует отслеживания цепочки принадлежности. Иными словами, любой компонент должен «знать» своего «владельца» и «родителя семейства».

В рамках VCL владельцем (Owner) любой компоненты формы является форма. Но некоторая группа компонент, в свою очередь, может принадлежать и другому компоненту – элементу интерфейса Windows, который называется «родителем» (Parent).

Пусть, например, при нажатии одной кнопки на форму необходимо поместить новую кнопку. По правилам VCL надо указать форму –«владельца» новой кнопки и ее «родителя», которым может быть некий компонент – владелец группы элементов управления. В простейшем случае форма выступает и как владелец, и как «группирующий» компонент, т.е. является одновременно и «родителем».

Код обработчика соответствующего события мог бы иметь такой вид:

 

procedure TForm1.Button1Click(Sender: TObject);

var

Button: TButton;

begin

Button:= TButton.Create(Self);

Button.Parent:= Self;

Button.Left:= 20;

Button.Top:= 20;

Button.Caption:= 'Click Me';

end;

 

В данном коде Self передается конструктору как фактический параметр (тем самым, свойство Owner новой кнопки будет указывать на форму, которой принадлежит кнопка). Далее свойству Parent созданной кнопки мы также присваиваем указатель на данную форму, поскольку другого группирующего компонента нет.

 

ПРИМЕЧАНИЕ: Выше было сказано, что классовые методы не имеют доступа к полям и методам класса. Это означает, что классовому методу (в отличие от методов класса) недоступен указатель Self.

 

 

Пример класса

 

Ниже приведен текст модуля, содержащий описание класса TAirplane. Класс TAirplane инкапсулирует задачу управления неким самолетом. Объекты этого класса можно было бы использовать в программе–симуляторе управления движением самолетов различного типа. Управление осуществляется посылкой «пилоту» той или иной команды из числа заранее оговоренных. В данном случае это команды «взлетайте», «садитесь», «измените (курс, высоту, скорость полета)».

Сперва изучите объявление класса и описания его методов. Затем мы обсудим детали.

 

 

unit AirplanU;

interface

uses

SysUtils;

const

{ Категории самолетов }

Airliner = 0; // трансконтинентальный авиалайнер

Commuter = 1; // для местных авиалиний

PrivateCraft = 2; // частный самолет

 

{ Константы состояния }

TakingOff = 0; // Взлетает

Cruising = 1; // В полете

Landing = 2; // Приземляется

OnRamp = 3; // На стоянке

 

{ Константы типов сообщений }

MsgChange = 0; // Изменить

MsgTakeOff = 1; // Взлететь

MsgLand = 2; // Приземлиться

MsgReport = 3; // Отрапортовать

 

type

TAirplane = class

private

Name: string; // Идентификатор самолета

Speed: Integer; // Скорость

Altitude: Integer; // Высота

Heading: Integer; // Курс

Status: Integer; // Состояние

Kind: Integer; // Тип

Ceiling: Integer; // Потолок

protected

procedure TakeOff(Dir: Integer); virtual;

procedure Land; virtual;

public

constructor Create(AName:string;AKind:Integer = Airliner);

function SendMessage(Msg:Integer; var Response: string;

Spd: Integer; Dir: Integer; Alt: Integer):Boolean;

function GetStatus(var StatusString:string):Integer;

overload; virtual;

function GetStatus: Integer; overload;

function GetSpeed: Integer;

function GetHeading: Integer;

function GetAltitude: Integer;

function GetName: string;

end;

 

 

implementation

 

constructor TAirplane.Create(AName: string; AKind: Integer);

begin

// Создает экземляр самолета типа AKind

// AName – идентификационный индекс самолета

// По умолчанию самолет находится на стоянке

inherited Create;

Name:= AName;

Kind:= AKind;

Status:= OnRamp;

// Установим потолок (Ceiling) в зависимости от типа c-та

case Kind of

Airliner: Ceiling:= 15000; //км

Commuter: Ceiling:= 10000; //км

PrivateCraft: Ceiling:= 5000; //км

end;

end;

 

procedure TAirplane.TakeOff(Dir: Integer);

// Реализует команду «Взлететь и лечь на курс Dir»

begin

Heading:= Dir;

Status:= TakingOff;

end;

 

procedure TAirplane.Land;

// Реализует команду «Посадка»

begin

Speed:= 0;

Heading:= 0;

Altitude:= 0;

Status:= OnRamp;

end;

 

function TAirplane.SendMessage(Msg:Integer;varResponse:

string; Spd:Integer; Dir:Integer; Alt:Integer):Boolean;

begin

Result:= True;

{ Do something based on which command was sent. }

case Msg of

MsgTakeOff:

{ На взлет не имеет смысла, если я уже в воздухе! }

if status <> OnRamp then

begin

Response:= Name + ': А я и так уже в воздухе!';

Result:= False;

end else

TakeOff(dir);

MsgChange:

begin

{ Отсеивание неверных команд на изменение параметров}

if Spd > 500 then

Response:='Ошибка: Скорость больше 500.';

if Dir > 360 then

Response:= Ошибка:Курс больше 360 градусов.';

if Alt < 100 then

Response:= Name + ': Разобьюсь!';

if Alt > Ceiling then

Response:= Name + ': Не могу забраться так высоко';

if (Spd = 0) and (Dir = 0) and (Alt = 0) then

Response:= Name + ': Не понял';

if Response <> '' then

begin

Result:= False;

Exit;

end;

{ Невозможно изменить состояние если ЛА на земле }

if status = OnRamp then

begin

Response:= Name + ': Я еще на земле.';

Result:= False;

end else

begin

Speed:= Spd;

Heading:= Dir;

Altitude:= Alt;

Status:= Cruising;

end;

end;

MsgLand:

{ Невозможно приземлиться, если и так на земле }

if status = OnRamp then

begin

Response:= Name + ': Я еще на поле.';

Result:= False;

end else

Land;

MsgReport:

begin

GetStatus(Response);

Exit;

end;

end;

{ Стандартная реакция, если все идет нормально. }

if Result then

Response:= Name + ': Все нормально';

end;

 

 

function TAirplane.GetStatus(var StatusString:string):Integer;

begin

StatusString:= Format('%s, Высота: %d, Курс: %d, ' +

'Скорость: %d', [Name, Altitude, Heading, Speed]);

Result:= Status;

end;

 

function TAirplane.GetStatus: Integer;

begin

Result:= Status;

end;

 

function TAirplane.GetSpeed: Integer;

begin

Result:= Speed;

end;

 

function TAirplane.GetHeading: Integer;

begin

Result:= Heading;

end;

 

function TAirplane.GetAltitude: Integer;

begin

Result:= Altitude;

end;

 

function TAirplane.GetName: string;

begin

Result:= Name;

end;

 

end.

 




Поделиться с друзьями:


Дата добавления: 2015-04-29; Просмотров: 721; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.108 сек.