Глава  10. ОБЪЕКТЫ

  10.1 Инкапсуляция 
  10.2 Наследование 
  10.3 Полиморфизм
  10.4 Динамические объекты
  10.5 Экспорт объектов
Тест по всей теме
оглавление
      В основе того или иного языка программирования лежит некоторая руководящая идея, оказывающая существенное влияние на стиль соответствующих программ.
Исторически первой была идея процедурного структурирования программ, в соответствии с которой программист должен был решить, какие именно процедуры он будет использовать в своей программе, а затем выбрать наилучшие алгоритмы для реализации этих процедур. Появление этой идеи было следствием недостаточной изученности алгоритмической стороны вычислительных процессов, столь характерной для ранних программных разработок (сороковые - пятидесятые годы). Типичным примером процедурно-ориентированного языка является Фортран - первый и все еще один из наиболее популярных языков программирования. Последовательное использование идеи процедурного структурирования программ привело к созданию обширных библиотек программирования, содержащих множество сравнительно небольших процедур, из которых, как из кирпичиков, можно строить «здание» программы.
    По мере прогресса в области вычислительной математики акцент в программировании стал смещаться с процедур в сторону организации данных. Оказалось, что эффективная разработка сложных программ нуждается в действенных способах контроля правильности использования данных. Контроль должен осуществляться как на стадии компиляции, так и при прогоне программ, в противном случае, как это показала практика, резко возрастают трудности создания крупных программных проектов. Отчетливое осознание этой проблемы привело к созданию Алгола-60, а позже - Паскаля, Модулы-2, Си и множества других языков программирования, имеющих более или менее развитые структуры типов данных. Логическим следствием развития этого направления стал модульный подход к разработке программ, характеризующийся стремлением "спрятать" данные и процедуры внутри модуля.
Начиная с языка Симула-67 [2], в программировании наметился новый подход, который получил название объектно-ориентированного программирования (ООП). Его руководящая идея заключается в стремлении связать данные с обрабатывающими эти данные процедурами в единое целое - объект. Характерной чертой объектов является инкапсуляция (объединение) данных и алгоритмов их обработки, в результате чего и данные, и процедуры во многом теряют самостоятельное значение. Фактически объектно-ориентированное программирование можно рассматривать как модульное программирование нового уровня, когда вместо во многом случайного, механического объединения процедур и данных акцент делается на их смысловую связь.
Какими мощными средствами располагает объектно-ориентированное программирование наглядно демонстрирует диалоговая среда Turbo Vision, входящая в комплект поставки Турбо Паскаля. Бели Вы захотите изучить ООП более подробно, советую познакомиться с библиотекой Turbo Vision.В этой главе мы рассмотрим основные идеи ООП и способы их использования.
Следует заметить, что преимущества ООП в полной мере проявляются лишь при разработке достаточно сложных программ. Более того, инкапсуляция придает объектам совершенно особое свойство «самостоятельности», максимальной независимости от остальных частей программы - правильно сконструированный объект располагает всеми необходимыми данными и процедурами их обработки, чтобы успешно реализовать требуемые от него действия. Таким образом, ООП в наибольшей степени подходит к задаче разработки библиотек программирования. Попытки использовать ООП для программирования несложных алгоритмических действий, связанных, например, с расчетными вычислениями по готовым формулам, чаще всего выглядят искусственными нагромождениями ненужных языковых конструкций. Такие программы обычно не нуждаются в структуризации, расчленении алгоритма на ряд относительно независимых частей, их проще и естественнее разрабатывать традиционными способами Паскаля. При разработке сложных диалоговых программ программист вынужден структурировать программу, так как только в этом случае он может рассчитывать на успех: «критической массой» неструктурированных программ является объем в 1000-1200 строк исходного текста - отладка неструктурированных программ большего объема обычно сталкивается с чрезмерными трудностями. Структурирование программы ведет, фактически, к разработке собственной библиотеки программирования - вот в этот момент к Вам на помощь и приходят новые средства ООП.
 
             Вверх

10.1. ИНКАПСУЛЯЦИЯ


   В Турбо Паскале средства объектно-ориентированного программирования связаны с тремя зарезервированными словами: OBJECT CONSTRUCTOR и DESTRUCTOR и двумя стандартными директивами: 
PRIVATE и VIRTUAL. 
   Зарезервированное слово OBJECT используется для описания объекта. Описание объекта должно помещаться в разделе описания типов, например: 
                   type 
                    Tpoint=object 
                    X,Y:Integer; {Координаты точки}
                    Color:word; {Цвет точки) 
                        Visible: Boolean; {Признак светимости} 
                  Procedure Setlocation(NewX, NewY: integer);
                         {Задает новое положение, точки на экране} 
                            Procedure SetCoforfNewColor: word); {Устанавливает цвет точки}
                            Procedure SetVislble(VIS: Boolean);
                                 {Выводит или гасит точку) 
                             Procedure GetLocatIon(var Xloc, YLoc:integer);
                       {Возвращает координаты точки}
                          Function GetColor: word;
                              {Возвращает цвет точки)
                               Function GetVislble: Boolean;
                          {Возвращает признак светимости точки} 
                                end; {Конец описания объекта ТРOINT)

   В этом примере описывается объект TPOINT, представляющий собой данные и методы (процедуры и функции), необходимые для работы с графическими точками на экране ПК. Заметим, что инкапсулированные объект процедуры и функции называются методами. Как видим, каждая точка характеризуется некоторым набором данных (своими координатами X и У, цветом COLOR и признаком светимости VISIBLE). Над этими данными определены все необходимые алгоритмические действия.    Если описать такой объект, то в программе можно предусмотреть сколько рсадно. экземпляров объекта, т.е. переменных типа объект. С помощью этих переменных можно осуществлять все предусмотренные, в объекте действия, например, для переменных типа TPOINT можно высветить или погасим, любую точку, переместить ее по экрану, изменить цвет. В следующем фрагменте программы точка перемещается на два пикселя вниз и вправо по экрану:
                  var 
                 POINT: TPoint; {Экземпляр объекта TPQINT}
                         X, Y : integer;
                        with Point do
                                 begin
                               if GetVislble then
                          SetVisibl(false); (Погасить точку)
                        GetLocation(X.Y); {Получить координаты}
                          SetLocatlon(X+2,Y+2); {Задать новые координаты}
                            SetVisible(True) {Зажечь точку)
                         end;
   Нетрудно заметить, что описание объекта и использование объектных переменных во многом похоже на описание и использование записей: инкапсулированные в объекте данные и методы становятся доступны с помощью оператора присоединения WITH или с помощью составных
имен, например:
if Point.GetVisible then Polnt.SetVisible(False);
   При описании объекта вначале, описываются все инкапсулированные в нем данные, а затем - методы доступа к этим данным. В качестве методов используются процедуры и функции, а также конструкторы и деструкторы (См. ниже). Сами методы при описании объекта, не раскрываются, указывается лишь их заголовок. Предполагаете», что они описываются где-то ниже по тексту программы, например 
                      type
                         Tpoint= object
                          ……………
                                 end; 
                         PROCEDURE TPoint.SetVlsible(Vis: Boolean);
                       {Реализация метода SETVISIBLE}
                            BEGIN
                           if Vis then begin
                          PutPIxeI(X,Y,Color); {высветить точку цветом Color)
                           Visible := True {Установить флаг светимости)
                        end 
                           else
                        begin
                        PutPixeI(X,Y.GetBkColor): {Погасить точку цветом фона)
                      Visible := False {Сбросить признак светимости)
                    End
                       END {TPolnt.SetVislble};

   Таким образом, указание методов при описании объекта подобно их опережающему описанию. Поскольку данные и методы инкапсулированы в одном объекте, все данные автоматически становятся глобальными, по отношению к любым методам и могут использоваться в них произвольным образом. Точнее говоря, любой метод объекта работает с невидимым оператором WITH, обеспечивающим ему доступ ко всем полям (как мы увидим далее, поля могут следовать в объявлении типа и после объявления метода и в этом смысле не являться для него глобальными). При обращении к методу ему передается особый идентификатор SELF, представляющий собой обобщенное имя экземпляра объекта.    Любой метод может использовать идентификатор SELF, чтобы явно указать на принадлежащий объекту метод или поле. Например:
            type ТСоо = record
              X.Y: integer; 
                end; 
                  TPoint = object
                X,Y: integer;
              Procedure lnit(Coo: TCoo);
               end;
                   Procedure TPofntfCoo: TCoo):
                      begin
                       with Coo do
                          begin
                              Self.X := X; 
                           Self.Y :=Y 
                            end
                        end;
 
                                         Вверх

10.2. НАСЛЕДОВАНИЕ


  Любой объект может быть объявлен потомком ранее описанного объекта. В этом случае он наследует все данные и методы объекта-родителя и может дополнять их своими данными и методами. Например, объект TLJNE, описывающий линию, можно объявить следующим образом:
type TPoint = object
end: {Конец описания объекта TPOINT)
TLIne = object(TPoint)
XE.YE: integer; {Координаты второго конца)
Procedure SetLineLocatIon(x1.y1,x2.y2: integer);
{Задает новое положение линии на экране) Procedure GetLineLocatlon(var x1l ,y11,x2l,y2l: Integer);
(Возвращает координаты линии) Procedure SetLIneVisible(vis: Boolean):
{Выводит или гасит линию)
end; {Конец описания объекта TLINE)

При объявлении объекта-потомка за словом OBJECT в круглых скобках указывается имя объекта-родителя. У объекта может быть сколько угодно потомков, но только один родитель.
При объявлении объекта-потомка TUNE перечислены лишь те данные и методы, которых недостает в объекте-родителе TPOINT, остальные TLINE автоматически наследует от своего родителя.
Таким образом, описание объекта-потомка TLINE эквивалентно такому описанию объекта TLINE_ANOTHER:
type
TLine_Another = object
X, Y Integer; {Координаты начала линии} 
Color word; {Цвет линии)
Visible Boolean: (Признак светимости)
XE.YE: Integer: {Координаты второго конца)
Procedure SetLocation(NewX, NewY: integer);
{Задает новое положение начала линии) 
Procedure SetColor(NewColor: word):
{Устанавливает цвет линии) 
Procedure SetVIsible(Vis: Boolean);
{Выводит или гасит линию)
Procedure GetLocation(var XLoc, YLoc: integer):
{Возвращает координаты начала линии)
Function GetColor: word;
{возвращает цвет линии)
Function GetVisible: Boolean;
{Возвращает признак светимости линии)
Procedure SetLIneLocationfxl.X1,Y1,x2 ,y2: integer);
{Задает новое положение линии на экране)
Procedure GetLineLocatlon(var x11,y1l,x2l,y2l):Integer);
{Возвращает координаты линии) 
Procedure SetLineVJsible(vis: Boolean);
{Выводит или гасит линию) 
end; {Конец описания объекта TLIne_Another) 

Из этого примера видно главное преимущество наследования: npи описании объекта-потомка Вам нет необходимости заново описывать уж существующие в объекте-родителе поля и методы. Потомок просто ж пользует их нужным образом для реализации требуемых от него действий Все, в чем нуждается потомок, - это описать специфичные для него поля методы, недостающие в объекте-родителе.
Наследование распространяется на любые объекты, в том числе и объекты-потомки: если в качестве родителя указано имя объекта, который сам по себе является потомком, новый объект наследует все свойства своего родителя и все свойства своих прародителей. Таким образом, наследование обеспечивает создание дерева родственных объектов.
Как и любое другое дерево, дерево объектов имеет «корень» - объект являющийся прародителем всех других объектов иерархии, и «ветви» порожденные от него потомки. По мере передвижения от корня к ветвям и перехода с ветви на ветвь объекты разрастаются в своих размерах, присоединяя к себе все новые и новые поля и методы. Если иерархия объектов хорошо продумана, на каждом ее уровне к объекту-родителю добавляются только необходимые поля и методы, поэтому в таком разрастании на самом деле нет ничего плохого. Более того, компоновщик Турбо Паскаля тщательно проверит откомпилированную программу и удалит из нее все лишнее - в том числе в исполняемую программу (ЕХЕ-файл) не будут включены методы, которые по каким-либо причинам не используются в программе.
Механизм наследования - это, пожалуй, самое мощное свойство ООП. Без наследования объекты превращаются в простую комбинацию данных и подпрограмм, не дающую качественных преимуществ по сравнению с традиционными для Паскаля процедурами и модулями. Наоборот, механизм наследования позволяет строить библиотеку по принципу «от простого - к сложному». Вводя с помощью наследования новый объект в свою библиотеку, программист в максимальной степени использует уже созданные (и, возможно, отлаженные) ранее объекты. Такой принцип конструирования программ называется восходящим программированием. В отличие от нисходящего программирования, он не дает возможности поэтапного создания программы. Мелкие детали реализации объектов заслоняют собою генеральный алгоритм, поэтому при использовании ООП рекомендуется сочетание подходов: методами нисходящего программирования генеральный алгоритм расчленяется на ряд относительно крупных и законченных в смысловом отношении фрагментов (модулей), а реализация этих фрагментов может основываться на объектно-ориентированном программировании.
Другое применение ООП - это разработка коммерческих библиотек программирования. Прекрасным примером такого рода применения ООП является уже упоминавшаяся библиотека Turbo Vision.

 
     Вверх

10.3. ПОЛИМОРФИЗМ


  Объект-потомок может не только дополнять поля и методы родителя, но и заменять методы родителя на новые (заменять поля родителя нельзя!). Например, вместо правила SETLJNEVISJBLE мы могли бы в объекте TLINE объявить правило SETVISIBLE, которое в этом случае перекроет (заменит собой) одноименное правило объекта-родителя TPOINT. В результате, к разным родственным объектам TPOINT и TUNE можно было бы применять одноименные правила SETVISIBLE, обеспечивающие сходные в смысловом отношении действия - показать или сделать невидимым графический объект. Свойства, позволяющее называть разные алгоритмические действия одним именем, называется полиморфизмом.
В следующем простом примере два родственных объекта владеют разными (но одноименными) методами OUT:

                       TYPE
                   ТА = object:
                     Procedure Out:
             end;
                  ТВ = obJect(TA)
               Procedure Out; 
                 end;
                  Procedure ТА.Out;
                    Begin
                        Write In ('МЕТОД ТА.OUT')
                      end;
                      Procedure ТВ.Out;
                     begin
                         Wrlteln('Метод ТВ.Out') 
                     end:
                        var
                          А: ТА;
                             В: ТВ;
                          begin
                               A.Out:
                                 B.Out
                         end.
    В результате прогона программы на экран выводятся строки: МЕТОД TA.OUT
Метод TB.Out
  В процессе компиляции будет установлена связь объекта с нужным методом, так что обращения A.OVT и В.OUT приводят к вызову разных методов. Такое связывание объектов и инкапсулированных в них полей с методами называется ранним, так как оно осуществляется на этапе компиляции. В Турбо Паскале существует возможность связывания данных с методами на этапе исполнения программы - такое связывание называется поздним. При позднем связывании в описании объекта соответствующий метод дополняется стандартной директивой VIRTUAL. Такие методы называются виртуальными. В отличие от этого методы, с которыми осуществлено раннее связывание (на этапе компиляции), называются статическими.
Появление директивы VIKTIJAL в объявлении метода как бы предупреждает компилятор: «Пока ты не знаешь, что я хочу. Придет время -запроси образец!». Встретившись с таким объявлением, компилятор не будет устанавливать связь объекта с методом. Вместо этого он создаст специальную таблицу, которая называется таблицей виртуальных методов (ТВМ). В этой таблице хранятся адреса точек входа всех виртуальных методов. Для каждого типа объекта создается своя ТВМ и каждый экземпляр объекта пользуется этой единственной для объектов данного типа таблицей. ТВМ обеспечивает работающую программу механизмом связывания объекта с полями. Фактическое связывание осуществляется с помощью обращения к конструктору - специальному методу, который во всем подобен обычной процедуре, но в заголовке вместо PROCEDURE содержит зарезервированное слово CONSTRUCTOR. В момент обращения к конструктору в специальное поле объекта заносится адрес нужной ТВМ, в результате чего все виртуальные методы (в том числе и унаследованные от родителей!) получают доступ к нужным полям.
Таким образом важным отличием позднего связывания от раннего является то обстоятельство, что при его использовании полиморфизм распространяется не только от текущего уровня иерархии вниз, к потомкам, но также и вверх, к родителям.
Чтобы продемонстрировать отличие раннего связывания от позднего, рассмотрим следующий пример. Пусть имеем такую программу:
                type
                 ТА = object Procedure Out;
                {Выводит сообщение методом OutStr}
               FunctIon OutStr: string;
              {Выдает строку 'МЕТОД ТА.OUTSTR"} 
                end;
                   ТВ =object(TA) 
                        Function OutStr: string:
                    {Выдает строку "Метод ТВ.OutStr'}
              end;
                     Procedure ТА.Out; 
                        begin
                              Writeln(OutStr)
                     end;
                        Function ТА.OutStr: string;
                               begin
                                    OutStr := 'МЕТОД ТА.OUTSTR'
                          end;
                        Function ТВ.OutStr: string;
                     begin
                  OutStr := 'Метод ТВ.OutStr'
                       end;
                              var
                           А: ТА; В: ТВ: 
                              Begin
                                  A.Out;
                                   B.Out 
                            end.
      В результате прогона на экран будет выведено: МЕТОД TA.OUTSTR МЕТОД TA.OUTSTR
Как и следовало ожидать, метод ТВ.OUTSTR не работает: ведь он статический и метод родителя TA.OUT просто не знает о его существовании.
Теперь рассмотрим вариант, использующий виртуальные методы:
                   type
                   ТА - object 
                           Procedure Out;
                       {Выводит сообщение методом OutStr} 
                           Function OutStr: string; virtual;
                           {Выдает строку "МЕТОД TA.OUTSTR"} 
                                  Constructor Init; 
                          end;
                      ТВ = object(TA) 
                           Function OutStr: string; virtual;
                            {Выдает строку "Метод ТВ.OutStr"}
                    Constructor Init:
                       end:
                   Procedure ТА.Out; begin
                Writeln(OutStr) end;
                      Function ТА.OutStr: string; begin
                   OutStr :- 'МЕТОД TA.OUTSTR' end;
                        Function ТВ.OutStr: string: begin
                      OutStr :• 'Ìåòîä ÒÂ.OutStr' end;
                          С
                               Constructor ТА.Init: begin end;
                                 Constructor ТВ. Init;
                                  begin
                           end;
                        var
                   А: ТА : В: ТВ; begin A. I nit: B.lnlt; A.Out: B.Out end. На экран будет выведено:
                  МЕТОД TA.OUTSTR
                    Метод TB.OutStr

      С помощью ТВМ родительский метод ТА.О1ГГ «узнал» о существовании виртуального метода TB.OUTSTR и использовал именно его при обращении B.OUT.
Конструктор может не обращаться к виртуальному методу и даже вообще быть пустым, т.е. не иметь никаких исполняемых операторов (как в нашем примере), тем не менее объект будет инициализирован правильно. Дело в том, что заголовок CONSTRUCTOR предписывает компилятору создать специальный набор машинных инструкций, который инициализирует ТВМ и исполняется в момент обращения к конструктору до выполнения его (конструктора) содержательной части. В объекте может быть сколько угодно конструкторов, но ни один из них не может быть виртуальным.
Обращение к статическим методам не требует инициализации объекта и может предшествовать обращению к конструктору. Однако без оператора
                   A.Init;
обращение
                  A.Out:
вызовет ошибку периода исполнения программы. Таким образом, если в объекте имеется хотя бы один виртуальный метод, в объект обязательно должен входить конструктор, причем обращение к конструктору должно предшествовать обращению к любому виртуальному методу.
Выбор того, каким именно - статическим или виртуальным - должен быть метод, зависит от специфики задачи и Ваших вкусов. Однако следует помнить, что статический метод никогда не может быть перекрыт виртуальным и наоборот. Список формальных параметров статического метода может отличаться от списка в перекрываемом методе, а для виртуальных методов оба списка должны быть идентичны. И, наконец, виртуальные объекты занимают несколько большую память (за счет ТВМ) и вызов виртуальных методов исполняется чуть медленнее, чем вызов статических. Тем не менее, всегда, когда это возможно, следует отдавать предпочтение виртуальным методам, т.к. они придают программе дополнительную гибкость. Всегда может оказаться, что рано или поздно Вы или кто-то из пользователей Вашей библиотеки захочет модифицировать ту или иную ее функции. В этом случае перекрытие виртуальных методов позволит предельно упростить задачу. Отметим, что стандартная функция Турбо Паскаля TypeOf(TObj) возвращает ссылку на ТВМ для объекта типа TObj. Эту ссылку можно использовать, например, для проверки того, с каким именно объектом работает в данный момент виртуальный метод: 
If TypeOf(Self) - TypeOf(TA) then ... 
И еще одно замечание. Между экземплярами родственных объектов возможен обмен информацией с помощью операторов присваивания. Например, если PoinTl и Point2 - экземпляры объекта TPOINT, то допустимо присваивание 

                           Point1 := Point2;
или 
                        PoinT2 := Point1;
Присваивания разрешены и между экземплярами родственных объектов разных уровней иерархии, однако в этом случае экземпляру объекта-родителя можно присвоить экземпляр потомка, но не наоборот! Например, разрешается присваивание
                         Point := Line;
но недопустимо
                           Line := Point; 
если LINE - потомок POINT. Это происходит по той причине, что потомок содержит все поля родителя, поэтому при присваивании потомка родителю эти поля получат правильные значения. Обратное же присваивание оставит без изменения «лишние» поля потомка, что является недопустимым.
При использовании виртуальных правил следует остерегаться присваивания между экземплярами объектов, во всяком случае необходимо помнить, что простое присваивание не заменяет собой инициацию виртуального объекта. Если, например, LINE и POINT - виртуальные объекты, то присваивание
                                 POINT:= Line;
не инициирует объект POINT, даже если объект LINE был перед этим инициирован. После такого присваивания необходим вызов конструктора объекта POINT перед обращением к любому виртуальному методу этого объекта.

 

                                    Вверх


    10.4. ДИНАМИЧЕСКИЕ ОБЪЕКТЫ


    Объектные переменные во многом подобны обычным переменным Турбо Паскаля, в частности, их можно размещать в динамической памяти. Турбо Паскаль содержит средства, облегчающие размещение объектных переменных в куче и их удаление из нее, например:
                       var 
                       PLINE: ^TLINE;
                                     New(Pline, Inlt);
    В этом примере размещение объектной переменной (на нее указывает PONE) в куче сопровождается одновременным обращением к конструктору TLINE.INIT для инициализации объекта и связывания виртуальных методов с вновь созданной переменной: в процедуре NEW допускается в качестве второго параметра указывать обращение к конструктору.
Более того, процедуру NEW можно вызывать и как функцию - в этом случае она возвращает значение типа POINTER, указывающее на динамически распределенный объект:
                              PLIne :=New(TLine); 
или
                                          PLine := New(TLIne, Init);
Обратите внимание: первым параметром процедуре New передается указатель на динамически распределяемый объект, в то время как первым параметром функции New - тип распределяемого объекта. И в том, и в другом случае в качестве второго параметра обращения допускается использовать вызов конструктора, однако имя конструктора не может быть составным - ведь в момент обращения динамический объект еще не создан. Например, оператор
                         New(Pline, Pline init);
вызовет сообщение об ошибке.
   При обращении к NEW с одновременным вызовом конструктора динамическая память резервируется с помощью специального программного кода, входящего в любой конструктор и вызываемого до начала работы исполняемой части конструктора (до begin). При этом динамическая память может оказаться исчерпанной. В этом случае стандартная функция обработки ошибок администратора кучи (см. п.7.7) выдает значение О, что вызывает аварийное завершение с кодом ошибки 203. Если используется нестандартная функция обработки ошибок и эта функция возвращает 1, конструктор пропускает операторы после begin и возвращает NIL. Таким образом гарантируется, что исполняемые операторы конструктора будут работать только при условии нормального распределения динамической памяти. Однако в теле конструктора может быть создан новый динамический объект, в нем - свой и т.д. Турбо Паскаль допускает произвольную глубину вложенности конструкторов. Если на каком-то уровне обнаружится нехватка динамической памяти, необходимо ликвидировать всю цепочку успешно распределенных объектов. Чтобы эта операция стала возможной, в Турбо Паскаль введена стандартная процедура без параметров FAIL, которая может вызываться только из конструктора и которая освобождает уже выделенную конструктором память, завершает его работу и возвращает NIL. 
Для удаления динамического объекта из кучи используется особыми метод - деструктор, описываемый с помощью зарезервированного слова DESTRUCTOR. В этом методе можно предусмотреть все действия, связанные с ликвидацией динамического объекта (т.е. переменной объектного типа, размещенной в динамической памяти), например, осуществить нужную коррекцию списка динамических объектов. Обращение к деструктору указывается вторым параметром при вызове процедуры DISPOSE, например:
                                    type
                                      TLi ne = object(Point)
                                 Constructor Init; Destructor Done: end;
                                   New(PLIne, Init); {Размещение динамического объекта)
                          Dispose(PUne, Done); (Удаление динамического объекта}
При необходимости деструктор, как и любой другой метод объекта (кроме конструктора), можно объявить виртуальным
 
                                   Вверх

10.5. ЭКСПОРТ ОБЪЕКТОВ


    Обычно объекты описываются в отдельном модуле, в интерфейсной части которого приводится объявление объекта, а в исполняемой части описание всех его методов. При желании часть инкапсулированных переменных и методов объекта Вы можете объявить скрытыми от пользователей Вашего модуля. Для этого используется стандартная директива                                                   PRIVATE:
                                                       type
                     Новый.обьект = object (Объект-родитель) 
                                      Доступные_поля; 
                                       Доступные_процедуры; 
                                               Private 
                                                        Закрытие-поля;
                                        Закрытие-процедуры;
                                    end;
     Все данные и методы, объявленные после директивы PRIVATE, доступны только внутри того модуля, в котором объявлен сам объект. Не смотря на то, что закрытие директивой PRIVATE поля располагаются после объявления доступных методов, любой метод объекта может использовать любое поле. Однако вне модуля доступ к закрытым полям невозможен. Хороший стиль объектно-ориентированного программирования рекомендует доступ к любым полям осуществлять только с помощью инкапсулированных методов. Использование директивы PRIVATE легко решает эту проблему при разработке коммерческих библиотек.
 
                     Вверх      оглавление

Хостинг от uCoz