Function TForml.GetClicks: Integer; begin
Result := FnClicks; end;
При детальном рассмотрении этого метода следует обратить внимание на два обстоятельства. Первое, метод класса имеет доступ к защищенному полю класса FnClicks. Второе обстоятельство менее очевидно и связано с рассматриваемой темой. При описании метода нельзя указать явно, поле какрго именно объекта используется для получения данных, например нельзя написать:
Result := Forml.FnClicks;
Понятно, что так нельзя сделать, поскольку метод является единым для всех объектов класса, а поля данных у всех объектов свои. Например, если будет описан другой объект класса TForml, например Form2, то необходимо, чтобы указанная строка имела вид:
Result := Fonn2 . FnClicks;
Решение вопроса как раз связано с параметром, который передается в любой метод, имеет идентификатор self, и, по сути, является ссылкой на тот объект класса, для которого вызван метод. Фактически, компилятор представляет текст метода следующим образом: function TForml.GetClicks: Integer; begin
Result := Self.FnClicks; end;
Если метод был вызван как Forml. GetClicks, то вместо self будет подставлен адрес объекта Forml. Таким образом, через параметр self вычисляются реальные адреса полей во время выполнения программы. (В С++ и Java для тех же целей используется ключевое слово this).
Пример. В Delphi ключевое слово Self часто используется, когда нужно явно обратиться к текущей форме в одном из ее методов. Типичный пример - динамическое создание компонента, когда в конструктор компонента Create необходимо передать параметр Owner (указать владельца), а затем присвоить то же значение свойству Parent (указать родителя). Тем и другим для создаваемого компонента является текущая форма, и лучшим способом указать на нее является использование self.
Пример 3.5. Обработчик события нажатия клавиши мыши на форме, который создает объект типа кнопка в том месте, где была нажата клавиша мыши
i>rocedure TForml. FormMouseDovm (Sender: TObject; Button:
TMousaButton; Shift: TShiftState; X, Y: Integer);
Var
Btn : TButton; begin
Btn := TButton.Create (self) Btn.Parent :» self; Btn.Left := X; Btn.Top := Y; Btn.Width := Btn.Width + 50; »tn.Caption := Format {'Кнопка на %d %d', [X, Y]) ; •rid;
Данный код создает кнопки там, где происходит щелчок мыши. Если вместо •elf передавать переменную Forml, то если в приложении появится несколько жземпляров формы ~ возникнет ошибка. Вообще, использование конкретного представителя класса в тех случаях, когда надо явно обратиться к текущему объекту, является плохим стилем программирования.
Конструкторы и деструкторы
Конструкторы - это специальные методы, создающие и инициализирующие объекты. Объявление конструктора выглядит как объявление метода, но предваряется ключевым словом constructor. В качестве имени конструктора обычно используют Create.
Type
TMyButton = class constructor Create;
And; var
Btn : TMyButton;
Если конструктор применяется к классу, то сначала происходит создание объекта - под него отводится память, значения всех полей «обнуляются», при тгом порядковым типам в качестве начального значения задается значение с номером 0, указателям присваивается nil, строки задаются пустыми, а поля типа Variant получают значение Unassigned. Затем выполняется код конструктора, в котором обычно описывается инициализация элементов объектов класса (присваивание полям и свойствам начальных значений и пр.) и иозвращается указатель на созданный экземпляр объекта. Например:
Btn := TMyButton.Create;
Конструктор может применяться и к объекту, например: Itn.Create;
В этом случае новый экземпляр объекта не создается, а выполняется только .ип'оритм конструктора. Фактически происходит переинициализация объекта. При и ом нельзя применять конструктор к еще неинициализированной переменной, поскольку память под нее еще не была распределена.
В реализации конструктора вначале обычно идет вызов конструктора класса предка с помощью ключевого слова inherited. В результате инициализируются все наследуемые поля. Затем в новом конструкторе инициализируются поля, введенные в данном классе.
Деструкторы - это специальные методы, уничтожающие объект и освобождающие место в оперативной памяти. Объявление деструктора выглядит также как объявление процедуры, но предваряется ключевым словом destructor. В качестве имени деструктора обычно задаютDestroy.
Реализация деструктора, как правило, завершается вызовом наследуемого деструктора с помощью ключевого словаinherited, чтобы освободить память от наследуемых полей.
Вместо того чтобы вызывать деструкторDestroy напрямую, принято вызывать специальную процедуруFree, действие которой состоит в том, что она вызывает деструкторDestroy только в том случае, если объект существует (то есть ссылка на него не равна nil). Следует учитывать, что методFree не устанавливает все ссылки на объект равными nil автоматически; если это необходимо, это должен делать сам программист, поскольку объект «не знает», какие переменные на него ссылаются.
При "рассмотрении конструкторов и деструкторов необходимо учитывать, что их нельзя использовать для компонентов Delphi (для объектов любых классов, произошедших от классаTComponent). Любой компонент, попавший при визуальном проектировании из Палитры компонентов в приложение, определяет своим владельцем форму (контейнер), на которую он помещен и для него конструкторы и деструкторы вызываются автоматически, при создании (уничтожении) владельца, то есть формы. Создание и уничтожение форм делает приложение (глобальный объект с именем Application), для этого в файле проекта вызывается методApplication. CreateForm.
§3.6. Наследование классов
Если необходимо создать новый класс, лишь немного отличающийся от уже существующего, то нет необходимости в переписывании заново уже существующих полей и методов. В этом случае можно объявить, что новый класс TNewCLass = class (TOldClass)
является потомком или дочерним классом существующего класса, называемого предком или родительским классом. Все классы Object Pascal имеют единого общего родителя - классTObject. Этот класс не имеет полей и свойств, но включает в себя методы самого общего назначения, обеспечивающие весь жизненный цикл любых объектов - от их создания до уничтожения.
Новый класс наследует методы, поля, свойства своего родителя. В новом классе обычно добавляются новые методы, поля и свойства. В случае если новое поле или свойство имеет идентификатор, совпадающий с идентификатором поля или свойства в родительском классе, то соответствующий элемент родительского
класса становится недоступным (перекрывается). Удалять объявленные в родительском классе поля, свойства и методы нельзя. 11ример 3.6. Наследование классов, type
TParentClass = class I: integer;
CounterValue: integer; end;
TChildClasa » class (TParentC:lass) CounterValue: longint; J: integer; end; var
Parent : TParentClass; Child : TChildClass;
В рассматриваемом примере объзктchild будет иметь три поля:
· X типа Integer - унаследованное из класса TParentClass;
· CounterValue типа Longing _ новое поле, которое перекрыло поле CounterValue типа Integer водительского класса;
· J типа Integer - новое поле, огределенное в классе TChildClass.
Для методов при наследовании действуют те же правила, что и для полей классов, за исключением совпаденю идентификаторов методов в классе-родителе иклассе-потомке. В этих случая: для методов определены более сложные механизмы переопределения (nepe:pbvrae, перегрузка и др.). Выбор того или иного механизма определяется видод метода, который задается при его описании специальными директивами. К пер^му ВИДу относятся статические методы, ко иторому - виртуальные (virtual) и динамические (dynamic) и, наконец, к фетьему - перегружаемые (overl<a<j) методы.
Модель наследования в Delphi hi поддерживает множественное наследование. ')то значит, что каждый класс може- иметь только один базовый класс. Частично реализовать множественное наслед^ание можно с использованием интерфейсов.
§3.7.Статические методы
Если при описании метода не задавались дополнительные директивы (по умолчанию), то метод является статическим. Если в классе-наследнике переопределить такой метод, то ДОИобъектов этого класса новый метод отменит родительский. Если обращаться кмехоДу этого класса, то вызываться будет новый метод. Но если обратиться к^ъекту, как к объекту родительского класса, го вызываться будет метод родителя
11ример 3.7. Пусть задан класс точе:TPoint и класс окружностей -TCircle,который является наследником tiacca точек. В обоих классах определен статический метод с именемDraw.
Interface
Type
TPoint = class X, 1 Integer;
constructor Create (a, b: integer); procedure Draw (Newx, Newy : integer); end;
TCircle = class (TPoint) Radius : Integer;