«Любой дурак может написать программу, которую поймет компилятор. Хорошие программисты пишут программы, которые смогут понять другие программисты»
23. Видимость компонентов класса
Каждый компонент класса имеет один из атрибутов, определяющих его область видимости (visibility of class members). Это одно из зарезервированных слов private, protected, public, published или automated. Видимость определяет, где и как компонент класса может быть доступен. Атрибут личный (private) определяет наименьшую видимость, защищенный (protected) – среднюю, а общедоступный (public), опубликованный (published) и автоматизированный (automated) – наибольшую.
Если никакой из атрибутов не указан, то по умолчанию предполагается published, если класс компилируется с директивой {$M+};в противном случае такие компоненты имеют атрибут public. Для большей читабельности рекомендуется группировать компоненты, имеющие одинаковую видимость, располагая их в таком порядке: private, protected, public, published и automated. Описание класса тогда имеет следующий вид:
type
TMyClass = class(TControl)
private
{ private declarations here}
protected
{ protected declarations here }
public
{ public declarations here }
published
{ published declarations here }
end;
Можно увеличить видимость компонента (только свойства property) в классе наследнике путем его повторного объявления (с другим атрибутом), однако понизить видимость нельзя. Например, компонент protected может быть сделан общедоступным (public) в наследнике, однако сделать его private невозможно. Более того, компоненты published не могут стать public в классе наследнике.
Компоненты private, protected и public.
Компоненты private невидимы вне того модуля или программы, где они описаны. Другими словами, метод private не может быть вызван из другого модуля, а поле или свойство private не могут быть прочитаны или изменены из другого модуля. Таким образом, доступ к этим компонентам из другого класса можно получить, если поместить описание этого класса в том же модуле. При таком описании на компонент надо ссылаться, естественно, с помощью указания имени экземпляра класса.
Компоненты protected видимы везде в том модуле, где объявлен класс, и в любом классе наследнике независимо от того, в каком модуле описан класс наследник. Этот атрибут присваивают тем компонентам, которые хотят сделать доступными только в классах потомках.
Компоненты published.
Видимость этих компонентов такая же, как и компонентов public. Разница состоит в том, что для опубликованных компонент генерируется информация времени выполнения (runtime type information RTTI). Эта информация позволяет приложению запрашивать поля и свойства объекта какого-либо неизвестного класса динамически. Delphi использует RTTI для доступа к значениям свойств при сохранении и загрузке файлов форм (.DFM), для вывода свойств инспектором объектов и для установки связи (соответствия) между обработчиками событий (event handlers) и специфическими свойствами, называемыми событиями (event).
Допустимыми типами опубликованных свойств являются:
· порядковые;
· строковые;
· классы;
· интерфейсы;
· указатели на методы;
· множество с числом элементов до 32 (порядковые значения 0-31);
· любой вещественный тип, кроме Real48.
Все методы являются публикуемыми, однако нельзя опубликовать два или более перегруженных метода.
Класс может иметь опубликованные компоненты, если он компилируется с директивой {$M+} или является производным от класса, который откомпилирован с этой директивой. Абсолютное большинство классов являются производными от класса TPersistent, который откомпилирован с директивой {$M+}, в связи с чем эту директиву редко приходится использовать.
При добавлении компонентов на форму Delphi помещает их в секцию published (этот атрибут видимости применяется по умолчанию).
Поля могут быть опубликованными, только если они имеют тип класс или интерфейс. Следующий пример иллюстрирует этот факт:
type
TMyClass = class
end;
TForm1 = class(TForm)
published
Num : integer; {Вызываетошибкукомпиляции: Published field ‘Num’ not a class
nor interface type. Поле такого типа надо описывать в другой секции}
MyClass : TMyClass;
Label1: TLabel;
Button1: TButton;
private
{ Private declarations }
public
{ Publicdeclarations }
end;
К вопросу о перекрытии описаний в производных классах.
Если в некотором модуле описать, например, объект типа TControl, то защищенные компоненты этого объекта будут недоступны, что естественно. Если теперь описать в этом же модуле класс, производный от TControl, то эти (защищенные) компоненты станут доступны, например:
TMyControl = class (TControl)
end;
Отметим, что доступными становятся все компоненты: поля, методы и свойства.
Почему так получается? Очевидно, потому, что в том модуле, где класс описан, все его собственные компоненты и унаследованные (в том числе и private) являются доступными. Если же описание класса TMyControl и объекта этого класса находятся в разных модулях, то спецификаторы доступа начинают работать так, как им и положено. В этом случае расширить область видимости компонент-свойств можно только за счет их повторного описания как общедоступных, например:
TMyControl = class (TControl)
public
property Font;
end;
Компоненты автоматизации (automated members).
Видимость этих компонент такая же, как и общедоступных. Отличие состоит в том, что для автоматических компонент генерируется информация типа автоматизации (Automation type information), которая требуется для серверов автоматизации. Компоненты автоматизации появляются обычно только в классах, производных от класса TAutoObject, объявленного в модуле OleAuto. Этот класс, как и само слово automated, предназначены только для обратной совместимости. Класс TAutoObject модуля ComObj не использует слово automated. На компоненты автоматизации накладывается целый ряд ограничений, на которых мы здесь останавливаться не будем.
Опережающие описания и взаимно-зависимые классы (Forward declarations and mutually dependent classes).
Если описание класса заканчивается словом class и символом ";" (точка с запятой), т.е. оно имеет форму
type className= class;
без указания родительского класса или списка компонентов, то это и есть опережающее описание класса.
Определяющее описание этого же класса должно быть сделано в том же разделе описания типа, что и опережающее описание. Другими словами, между опережающим и определяющим описанием класса не может находиться ничего, кроме описания других типов.
Опережающее описание позволяет описать взаимно-зависимые классы, Приведем пример:
type
TFigure = class; // Опережающее описание
TDrawing = class
Figure: TFigure;
//F i e l d s
end;
TFigure = class // Определяющее описание
Drawing: TDrawing;
end;
Не путайте опережающее описание класса с определяющим описанием класса, который является производным классом от TObject, но для которого не указан родительский класс или указан класс TObject:
type
TFirstClass = class; // Опережающее описание
TSecondClass = class // Определяющее описание
end; //
TThirdClass = class(TObject); // Определяющее описание
«22. Введение в классы и объекты»
24. Поля и методы классов