Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Как наследовать метод не родителя, а родителя родителя? [D3, D5, Win2k]


Knyaz17 ©   (2001-12-21 11:47:00) 

Господя, подскажите пожалуйста, как можно отнаследоавть метод не родителя, а родителя родителя? То есть есть три класса class1, Class2, Class3.
Они отнаследованы в порядке перичисления, есть метод в Class3.
Как сделать чтобы выполнялся метод в Class1, а потом сразу в Class3???


Knyaz17   (2001-12-21 13:43:00) [1]

Всем большое спасибо за участие в дискуссии, а особенно Юрию Зотову!!!


Юрий Зотов   (2001-12-21 13:39:00) [2]

> Алексей Петров © (21.12.01 12:50)

> Это уже чистое хаккерство, основанное на не документированном факте,
> что экземпляр объекта начинается с указателя на VMT.

Почему недокументированном? Вот цитата из хелпа Delphi 5 (ключевое слово для поиска - VMT)

"The first 4-byte field of every object is a pointer to the virtual method table (VMT) of the class."

Значит, все вполне легально. И в будущих версиях не изменится.


Knyaz17   (2001-12-21 13:32:00) [3]

Всем большое спасибо за участие в дискуссии, а особенно Юрию Зотову!!!


Юрий Зотов   (2001-12-21 13:27:00) [4]

> Knyaz17 (21.12.01 13:01)

А что мешает использовать тот же прием в TClass1.Go и в итоге вызвать из него TClass3.Refresh? Более того, метод Go можно определить с параметром:

procedure Go(AClass: TClass = nil); virtual;

Тогда TClass3 вызывает его, как Go(OldClass), а TClass1 в итоге получает именно тот класс, чей метод Refresh надо вызвать.


Владислав   (2001-12-21 13:18:00) [5]

Привел бы реальный код (часть его).


Knyaz17   (2001-12-21 13:01:00) [6]

Предыдущий мой ответ был ответом на сообщение Юрия Зотова.


Knyaz17   (2001-12-21 12:57:00) [7]

Да, но не годится, если в методе класса1 другой такой же наследуемый во всех классах метод!!!
То есть у меня есть:

TClass1 = class(...)
procedure Go; override;
procedure Refresh; override;
end;

TClass2 = class(TClass1)
procedure Go; override;
procedure Refresh; override;
end;

TClass3 = class(TClass2)
procedure Go; override;
procedure Refresh; override;
end;

и метод Refresh срабатывает в Go для Class1!!!, а в Class3 он переопределен, и должен срабатывать.


McSimm   (2001-12-21 12:53:00) [8]

Извиняюсь за непроверенный ответ.
Перепутал с конструктором.


Alx2   (2001-12-21 12:53:00) [9]

Жуть :)))
Можно, конечно, но стоит ли так извращаться через RTTI?
Может, использовать object вместо class. Там можно напрямую вызвать метод любого из предков. Хотя, конечно, теряются другие прелести.
Но, скорее всего, такие проблемы возникают из-за неподходяще построеной иерархии классов.


Алексей Петров   (2001-12-21 12:50:00) [10]

> Юрий Зотов.
Это уже чистое хаккерство, основанное на не документированном факте, что экземпляр объекта начинается с указателя на VMT.
Чисто теоретически, в очередной версии Delphi это может перестать работать. Хотя я почти уверен, что Borland этот факт оставят верным "на вечно" :)

К стати, методика годится и для вызова динамическогометода дедушки без изменений.


Юрий Зотов   (2001-12-21 12:34:00) [11]

Привожу работающий пример (см. книгу Сергея Орлика "Секреты Delphi на примерах"). В проекте - три формы, отнаследованные одна от другой по цепочке. При нажатии на кнопку показывается сообщение с именем формы. В классе TForm3 перед этим вызывается виртуальный метод "деда" (именно так, сразу "деда" - напрямую, а не через "отца"). Что мы и видим на экране.

Фокус в том, что перед вызовом метода подменяется вход в VMT (потом он, конечно, должен быть восстановлен). В итоге класс TForm3 как бы временно становится классом TForm1. Причем таким образом можно вызвать вообще любой метод любого класса, независимо от отношений наследования.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, PInfoViewer, Grids, DBGrids;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
public
procedure VirtualMethod; virtual;
end;

var
Form1: TForm1;

implementation

uses
Unit2, Unit3;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
VirtualMethod
end;

procedure TForm1.VirtualMethod;
begin
ShowMessage("Form1")
end;

procedure TForm1.FormShow(Sender: TObject);
begin
Form2.Show;
Form3.Show
end;

end.

=========================================================================

unit Unit2;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Unit1, StdCtrls;

type
TForm2 = class(TForm1)
public
procedure VirtualMethod; override;
end;

var
Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.VirtualMethod;
begin
ShowMessage("Form2")
end;

end.

=========================================================================

unit Unit3;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Unit2, StdCtrls;

type
TForm3 = class(TForm2)
public
procedure VirtualMethod; override;
end;

var
Form3: TForm3;

implementation

{$R *.DFM}

type
PClass = ^TClass;

procedure TForm3.VirtualMethod;
var
OldClass: TClass;
begin
OldClass := PClass(Self)^;
PClass(Self)^ := ClassParent.ClassParent;
try
VirtualMethod
finally
PClass(Self)^ := OldClass
end;
ShowMessage("Form3")
end;

end.


Knyaz17   (2001-12-21 12:33:00) [12]

To Digitman:

у меня есть структура модулей и в них не 3, а много классов наследуются,
и организовано это следующим образом:

TClass1 = class(...)
procedure Go; override;
end;

TClass2 = class(TClass1)
procedure Go; override;
end;

TClass3 = class(TClass2)
procedure Go; override;
end;

соответственно мне не надо вызывать метод Go класса Class2, но желательно
только в Class3.


Knyaz17   (2001-12-21 12:22:00) [13]

Или что делать если метод не виртуальный?
Можно сделать как нибудь через конструкцию
inherited?

что-то типа
procedure TClass3.Go;
begin
inherited TClass1.Go;
end


Digitman   (2001-12-21 12:20:00) [14]

не нужно TClass1.Go делать виртуальным.

TClass1 = class(...)
procedure Go;
end;

TClass2 = class(TClass1)
procedure Go;
end;

TClass3 = class(TClass2)
procedure Go;
end;

procedure TClass3.Go;
begin
TClass1(Self).Go;
TClass2(Self).Go;
Go;
end;


Knyaz17   (2001-12-21 12:16:00) [15]

А как мне объявить виртуальный (динамический) метод?
Я делаю в Classs1:
procedure Go; virtual;

а в Class2, Class3:
procedure Go; override;

и ничего не получается, если я делаю:
procedure TClass3.Go;
begin
TClass1.Go;
end
то компилятор ругается.


Knyaz17   (2001-12-21 12:03:00) [16]

To Ю Ю:
Остальные методы, кроме этого, нужно наследовать от Class2.


McSimm   (2001-12-21 12:03:00) [17]

Если имеется в виду виртуальный (динамический) метод
и если я не ошибаюсь,
то можно

procedure TClass3.SomeMethod;
begin
// Ваш код
TClass1.SomeMethod
// Ваш код
end


Ю Ю   (2001-12-21 11:58:00) [18]

Значит Class2 вовсе и не родитель Class3, раз его метод не удовлетворяет наследника :-) Наследуй непосредственно от Class1


Юрий Зотов   (2001-12-21 11:57:00) [19]

Уточните - имеется в виду виртуальный (динамический) метод?


Наверх

Титульная страница Поиск, карта сайта Написать письмо