type

tMyObject=

class(tObject)

...

property Strings[I:Integer]:string

read GetS

write PutS; {!!!после PutS обязательно ";"}

default;

...

end;

var aMyObject:tMyObject;

begin

...

aMyObject. Strings[1]:='обычный способ';

...

aMyObject[2]:='сокращенная форма';

...

end.

Векторные свойства могут быть многомерными. При этом порядок следования индексных параметров и их сигнатура должны соответствовать индексам массива, а в методе по записи после индексных параметров должен стоять параметр (такой же размерности и того же типа, что и свойство) для передачи свойству значения. Этот параметр может передаваться по имени или по значению. Как правило, для векторных свойств используется передача по имени, т. к. при передаче по значению массив должен быть продублирован, что занимает много оперативной памяти и времени.

В Delphi предусмотрен еще один способ передачи параметров в процедуру: передача константы по имени (пишется директива const вместо var перед именем переменной). Все происходит так же, как при директиве var, но при этом внутри процедуры не может быть изменено значение этой переменной (как при передаче по значению). На этапе компиляции отслеживаются ошибочные попытки изменить значения переменной путем присвоения или передачи параметра по имени в какую-либо процедуру.

Пример двумерного свойства:

property MyMatrix[Row, Col:Integer]:real

read GetElement

write SetElement;

Описание методов в этом случае может быть таким:

function GetElement(Row, Col:Integer):real;

НЕ нашли? Не то? Что вы ищете?

procedure SetElement(Row, Col:Integer; const NewValue:Real);

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

4.10. Создание и уничтожение объектов. Конструкторы и деструкторы.

В Object Pascal объекты могут быть только динамическими! Любая переменная объектного типа — это указатель, но доступ к объекту идет без 'шляпки', а создание нового объекта осуществляется не оператором New, а с помощью вызова конструктора — функции, возвращающей указатель на создаваемый объект. Соответственно, уничтожение объекта производится не с помощью Dispose, а вызовом деструктора. Поэтому обычно в Delphi конструктор называют Create ("создать"), а деструктор — Destroy ("уничтожить, разрушить"):

aMyObject:=tMyObject. Create; {создали объект aMyObject типа tMyObject

и запомнили указатель на него}

...

aMyObject. Destroy; {уничтожили объект}

Заметим, что конструктор — это метод класса, и он работает без экземпляра объекта (экземпляра еще нет, но метод создает его). В Delphi конструкторы могут быть виртуальными, и их может быть несколько. Сразу при входе в конструктор желательно проинициализировать прародителя:

constructor tMyObject. Create;

begin

inherited Create;

...

end;

В данном примере инициализация прародителя осуществляется с помощью специального ключевого слова inherited, которое появилось еще в TurboPascal 7.0. Оно означает, что следующий за ним вызов относится к непосредственному прародителю данного объекта. Многократные вызовы типа inherited inherited запрещены, чтобы не вызвать неправильной инициализации объекта.

Если наш класс описан как

tMyObject=class(tObj1)

...

end;

то вызов inherited Create абсолютно логичен вызову tObj1.Create.

4.11. Наследование. Статические, виртуальные, динамические и абстрактные методы.

Все классы Delphi — потомки класса tObject. Если нет указания, какой класс является прародителем, значит, это tObject. Определение

tMyObject=class

...

end;

полностью равносильно

tMyObject=class(tObject)

...

end;

Вызываемые методы делятся на статические (static), виртуальные (virtual) и динамические (dynamic). Есть еще методы, которые не содержат никаких действий (нет реализации), никогда не вызываются и нужны только для наследования — абстрактные (abstract). Абстрактными могут быть только виртуальные или динамические методы, причем у них имеется только заголовок (в описании класса) и отсутствует реализация.

Переопределение методов в потомках называется перекрытием этих методов, а сами переопределенные методы прародителя — перекрытыми (override) потомком.

Перекрытие статических методов происходит так же, как в Turbo Pascal. Для виртуальных методов синтаксис изменен: в первом из прародителей, где определяется виртуальный метод, ставится ключевое слово virtual, а в потомках для перекрывающих методов — override.

Для виртуальных методов в классе постоянно хранятся адреса всех имеющихся в данном классе виртуальных методов независимо от того, унаследованы они или перекрыты. Виртуальные методы вызываются быстро, но таблица виртуальных методов (VMT) может занимать много места, если имеется длинная цепочка наследования с большим количеством методов в каждом классе. Динамические методы являются более экономичными по памяти и более медленным аналогам виртуальных. Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов (DMT) класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе. Если при вызове нужный метод в таблице не найден, просматривается таблица прародителя, и т. д. вплоть до tObject. В остальном разницы между виртуальными и динамическими методами нет. Стоит отметить, что Delphi предоставляет существенно больший набор средств программирования, чем, например, Java, где ни статических, ни виртуальных методов нет, а только динамические.

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

Пример использования виртуальных и абстрактных методов:

type

tField=

class function GetData:string;virtual;abstract; {нужна для работы

ShowData1}

tStringField=

class(tField)

fData:string;

function GetData:string;override;{перекрыли абстрактный метод}

end;

tIntegerField=

class(Field)

fData:Integer;

function GetData:string;override; {то же}

end;

...

реализация перекрывающих методов

function tStringField. GetData:string;

begin

GetData:=fData; {присваивание строкового поля}

end;

function tIntegerField. GetData:string;

begin

GetData:=IntToStr(fData); {преобразование из целого в строку}

end;

...

процедура, которая работает для разных типов полей

procedure ShowData(aField:tField);

begin

Form1.Label1.Caption:=aField. GetData;{вызов метода, который для

класса tField абстрактный}

end;

Надо отметить, что на этапе написания процедуры ShowData мы используем алгоритм для экземпляра класса, в котором описан наш абстрактный метод. Такой класс логично называть абстрактным и считать, что экземпляров данного класса быть не может. На деле, вместо экземпляров абстрактного класса будут подставляться экземпляры классов-потомков, в которых все абстрактные методы перекрыты. А формальное использование объекта из прародительского класса дает возможность писать единый алгоритм для всех классов-потомков разветвляющейся иерархии с разными типами аналогичных по смыслу полей в каждой ветви иерархии.

Особенность нового синтаксиса для объектов — то, что динамически созданный объект как параметр можно передавать как по значению (см. ShowData), так и по имени (см. далее ShowData1):

procedure ShowData1(var aField:tField);

begin

...

end.

При этом в случае передачи по значению котируется только указатель на объект, но не его тело. В Object Pascal, в отличие от C++, нет понятия "конструктора копии", и при необходимости скопировать тело объекта надо просто задать метод объекта, который занимается его копированием.

4.12. Области видимости объектов.

В Delphi существуют зарезервированные слова public ("общие"), protected ("защищенные") и private ("личные") для определения специальных правил видимости областей кода в объектах. Кроме того, для работы в среде Delphi используется еще зарезервированное слово published ("опубликованные"), являющееся вариантом public. Начиная с Delphi 3.0 при описании класса поля, свойства и методы должны располагаться в области, помеченной как published, public, protected и private.

Поля, свойства и методы доступны:

public, published — всем и везде, в том числе в других модулях;

protected — только в описании методов в классах потомков (в том числе в других модулях);

private — только в процедурах и функциях модуля, где описывается данный класс, а также в методах классов потомков, определенных внутри этого модуля.

Разновидностью public является published. "Опубликованные" поля, методы и свойства доступны в интерфейсе визуального проектирования Delphi (в инспекторе объектов) на стадии разработки. В первых версиях Delphi действовало правило, что если в описании класса были опущены ключевые слова для определения правил видимости, считалось, что соответствующие методы, поля и свойства published. Начиная с Delphi 3.0 область видимости объекта "published" также должна быть явно описана. Т. е. во всех примерах, где мы работали с классами, надо поставить директиву public или published перед описанием полей и методов объекта. Например:

tMyObj1=class(tObj1)

...

end;

заменить на

tMyObj1=class(tObj1)

public

...

end;

Пример правил видимости внутри модуля:

unit vis1;

interface

type

tObj1=

class

public

procedure MyPublicMethod;

...

protected

procedure MyProtectedMethod;

...

private

procedure MyPrivateMethod;

...

end;

procedure MyProc1; {процедура, видимая в других модулях}

...

секция реализации

implementation

... {реализация tObj1.MyPublicMethod, tObj1.MyProtectedMethod,

tObj1.MyPrivateMethod}

...

...

var aObj1:tObj1;

procedure MyProc1;

begin

aObj1:=tObj1.Create;

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17