Помощь - Поиск - Пользователи - Календарь
Полная версия: Проблема с ООП
Форум «Всё о Паскале» > Pascal, Object Pascal > Теоретические вопросы
LHx
В общем, поясняю. У меня есть объект obj, содержащий метод move, который, в свою очередь, использует метод collide. От obj происходит объ. dot, тоже использующий НЕизмененный метод move, который _должен_ использовать измененный метод dot.collide, однако использует метод obj.collide. Видимо, для использования метода dot.c..... придется перекрывать метод move, а это долго. Есть методы без извращений?
NetAnton
"Баг с наследованием" - ну это не баг. а чтобы использовать метод предка, используй inherited move. но это, конечно, вызовет и родительский collide.
jaros
Проблема решается с полпинка, если сделать метод collide виртуальным.

type
obj = object
...
procedure move;
procedure collide; virtual;
...
end;
dot = object (obj)
...
{ процедуры move нету -- она родительская }
procedure collide; virtual;
...
end;


Всё! Теперь, когда move вызывает метод collide, всегда вызывается правильный collide, а не только родительский.

См. также Виртуальные функции и работа с ними
NetAnton
пробовал виртуальные, дык Паскаль вылетает... angry.gif
jaros
huh.gif Как это вылетает? Не должен же вроде.

Ну-ка, с этого места поподробнее...
BlackShadow
Объясняю популярно.
Во-первых:
Цитата
чтобы использовать метод предка, используй inherited move. но это, конечно, вызовет и родительский collide

Не правда. Ни к чему такому это не приведёт.

Во-вторых. Паскаль действительно может вылетать при работе с объектами. Причиной это то, что в какоё-то момент идёт попытка вызова виртуального метода, а конструктор не ьыл вызван, т. е. VMT содержит полный бред!
jaros
Ну тогда конечно... Кто же пишет объектно-ориентированно, но без виртуальных функций и конструкторов/деструкторов? По-моему, уж лучше тогда писать "как на C" -- т.е. без классов и наследования.

Извращенское решение поставленной задачи: заведите себе в классе obj переменную, которая будет показывать, объект ли это типа obj или всё-таки dot. Не забудьте их проинициализировать. В процедуре collide, которая должна быть только у obj, делаем проверку. Если переменная имеет значение 1, выполняем одни действия, если 2, то другие.

Правильное (то есть объектно-ориентированное) решение: использовать виртуальные функции, завести конструктор и деструктор, создавать объект только посредством New, удалять объект после использования посредством Dispose. Научиться работать с указателями (хотя я надеюсь, что это вы знаете).

Могу рассказать поподробнее.
BlackShadow
jaros, ну не обязательно лезть в динамическую память. Можно и по-проще. Например, если класс TMyObject имеет конструктор Create и деструктор Destroy, то можно просто и банально:
Код

Var
 a:TMyObject;
Begin
 a.Create;
 {...}
 a.Destroy
End.

Яркий пример - TV:
Код

Application.Init;
Application.Run;
Application.Done

, если меня память не подводит.

А вообще я не совсем понял твой "извращенский" вариант. Есть TypeOf - прекрасно подходит для проверки типа, а, что ты там ещё предложил... Ну это неважно. Главное ты сказал: классы - рулез, а наследование - неотъемлимая часть!
jaros
TypeOf на самом деле возвращает указатель на таблицу виртуальных функций. Если он не проинициализирована конструктором (как у товарища), то typeof должен вернуть чушь. Сравнивать одну чушь с другой и надеяться, что они совпадут или не совпадут именно когда надо, я бы не стал... Но после конструктора всё должно быть в порядке.
BlackShadow
Исключение составляет TypeOf(TMyObject), который берёт инфу совсем не из поля VMT smile.gif
LHx
Поясните балде, что значат директивы constructor, destructor и virtual. blink.gif
virt
грубо говоря ,конструктор -- метод который вызывается при создании объекта ,деструктор -- при уничтожении объекта.
виртуальные методы -- хз.
BlackShadow
Конструктор - штука интересная. По сути это процедура, которая вызывается не ПРИ создании, а ДЛЯ создания объекта. Он имеет Параметр №0 и строчку №0. Параметр №0, это как в любом другом методе - Self, то есть ссылка на экземпляр класса, вызвавшего метод. Сточка №0 куда интереснее. Она проверяет Self на Nil. Если это так, то объект располагается в динамической памяти. В таком случае нужно ему её выделить, что конструктор и делает. Затем смотрит, имеет ли этот класс виртуальные методы. Если имеет, то заполняет его поле VMT (поле №0 smile.gif ) адресом VMT Этого класса. Затем уж происходит вызов того, что ты сам насочинял. Параметр Self к этому времени имеет уже корректное значение.
Мало того конструктор - единственная функция, которая может быть вызвана без сохранения результатов при любой настройки компилятора. Да-да, именно функция. И та необычная. Её результат можно использовать как Boolean (для проверки вызова Fail в теле конструктора), как Pointer, чтобы узнать где расположился твой объект, и как указатель на экземпляр какого-то там класса.

Деструктор - штука более банальная. Он вызывается ПРИ уничтожении объекта и служит как правило для освобождения ресурсов занятых под нужды объекта. Например для класса списка, логично освобождать в нём память, а для файла - закрывать его.

Виртуальные функции - основа ООП. Вот тебе пример из ЛЮБОЙ книжки по паскалю, где вообще этот вопрос задевается:
Код

Uses Graph,CRT;

Type
 TFigure=Object
   x:Integer;
   y:Integer;
   Constructor Create(ax,ay:Integer);
   Procedure Draw;Virtual;
   Destructor Destroy;Virtual;
 End;

 TRound=Object(TFigure)
   r:Integer;
   Constructor Create(ax,ay,ar:Integer);
   Procedure Draw;Virtual;
   Destructor Destroy;Virtual;
 End;

 TRect=Object(TFigure)
   a:Integer;
   b:Integer;
   Constructor Create(ax,ay,aa,ab:Integer);
   Procedure Draw;Virtual;
   Destructor Destroy;Virtual;
 End;

Constructor TFigure.Create(ax,ay:Integer);
Begin
 x:=ax;
 y:=ay
End;

Procedure TFigure.Draw;
Begin
End;

Destructor TFigure.Destroy;
Begin
End;

Constructor TRound.Create(ax,ay,ar:Integer);
Begin
 Inherited Create(ax,ay);
 r:=ar
End;

Procedure TRound.Draw;
Begin
 SetColor(Red);
 Circle(x,y,r)
End;

Destructor TRound.Destroy;
Begin
 SetColor(Black);
 Circle(x,y,r)
End;

Constructor TRect.Create(ax,ay,aa,ab:Integer);
Begin
 Inherited Create(ax,ay);
 a:=aa;
 b:=ab
End;

Procedure TRect.Draw;
Begin
 SetColor(Red);
 Rect(x,y,x+a,x+b)
End;

Destructor TRect.Destroy;
Begin
 SetColor(Black);
 Rect(x,y,x+a,x+b)
End;

Var
 a:Array[0..99] Of ^TFigure;
 i:Integer;

Begin
 {InitGraph там какой-нибудь...}
 For i:=Lo(a) To Hi(a) Do
   If Random(2)=1 Then
     a[i]:=New(TRound,Create(Random(GetMaxX),Random(GetMaxY),Random(100))
   Else
   a[i]:=New(TRect,Create(Random(GetMaxX),Random(GetMaxY),Random(100),Random(100));
 For i:=Lo(a) To Hi(a) Do
   a[i]^.Draw;
 ReadKey;
 For i:=Lo(a) To Hi(a) Do
   Dispose(a[i],Destroy);
 ReadKey;
 RestoreCRTMode
End.

Уф... Запарился пока писал. Попробуй разберись сам - тут всё примитивно. Если что - спрашивай. Суть виртуальных примерно такая - они могут заменять или дополнять функции предков. В этом примере конструктор (он всегда типа виртуальный) дополняет метод предка, а процедура Draw - заменяет.

З. Ы. : а вообще, Oleg_Z писал введение в ООП и закреплял его, по-моему. Там-то должно быть про это всё рассказано.
Romtek
virtual; - когда нужно перекрыть метод.
Например, есть метод Draw для отрисовки какой-то фигуры.
Если необходимо изменить только отрисовку объекта, то нет нужды писать новый объект, а надо просто "перекрыть" новым методом.
Код
Type
 TCircle = object (TGraphObject)
    procedure Draw;
 end;

 TFilledCircle = object (TCircle)
   filled: boolean;
   procedure Draw; virtual; { перекрытие метода }
 end;

procedure TFilledCircle.Draw;
begin
 if Filled then
    Inherited.Draw { рисуем окружность }
 else
 begin
   { рисуем заполненную окружность (круг) }
 end;
end;
BlackShadow
Не обязательно перекрыть! Можно просто дополнить. Например:
Код

Type
 TCollection=Object
   {...}
   Procedure Add(Item:SomeType);Virtual;
   {...}
 End;

 TSortedCollection=Object(Collection)
   Procedure Sort;
   Procedure Add(Item:SomeType);Virtual;
 End;

Procedure TCollection.Add(Item:SomeType);
Begin
 {...}
End;

Procedure TSortectCollection.Sort;
Begin
 {...}
End;

Procedure TSortedCollection.Add(Item:SomeType);
Begin
 Inherited Add(Item);  {Вызов метода предка}
 Sort
End;

{...}

Romtek
Тему в "Теоретические" ?
BlackShadow
Согласен. Тут она не в теме smile.gif
w0w
никак не понял где faq. ну ладно.
хочу научиться использовать меню.. паскаль сказал, что есть на этот счет объект TMenuView, но образца не нашел.
и не работал с объектами както раньше.
не напишите коротенький код для простейшего меню?

Не в тему. Создай свой топик в нужном разделе.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.