Форум: "Потрепаться";
Текущий архив: 2003.01.27;
Скачать: [xml.tar.bz2];
ВнизПереопределение методов Найти похожие ветки
← →
Tsr (2003-01-07 21:56) [0]Возникает большой вопрос с сабжем. Везде и всюду пишется, что переопределять можно только виртуальные и динамические методы с применением директивы override.
Но смотрим хотя бы на реализацию класса Exception:
Exception = class (TObject)
...
public
constructor Create(Const Msg: string);
...M
Переопределением и не пахнет. Как я понимаю, происходит не переопределение, а перекрытие конструктора Create объекта TObject.
1) Если это так - вопрос. А чем это грозит ?
2 )Зачем вообще использовать override ?
Ведь можно просто перекрыть метод (в том числе и статический) и вызвать конструктор предка.
Пример.
TA=class
procedure SomeProcedure; dynamic
end;
...
TB=class(TA)
procedure SomeProcedure;override
end;
...
procedure TB.SomeProcedure;
begin
inherited
//Что-нибудь этакое
end;
Тут переопределение по всем правилам.
Но чем грозит такой, вроде бы аналогичый вариант ?
TA=class
procedure SomeProcedure;
end;
...
TB=class(TA)
procedure SomeProcedure;
end;
...
procedure TB.SomeProcedure;
begin
inherited
//Что-нибудь этакое
end;
← →
Оливейра (2003-01-07 22:43) [1]КОнструкторы не МОГУТ БЫТЬ виртуальными (Вирт в гробу переворачивается :-)) Это как - объекта, значит, нету, да? а его VMT уже - есть??? Конструктор ВСЕГДА невиртуальный метод!
← →
Tsr (2003-01-07 23:13) [2]Оливейра действительно:
TObject = class
constructor Create;
Но почему тогда конструктор Create переопределяют с помощью override ?
Также хотелось бы услышать ответы на два поставленных вопроса с объяснениями примеров
← →
Юрий Зотов (2003-01-07 23:52) [3]> Оливейра © (07.01.03 22:43)
> КОнструкторы не МОГУТ БЫТЬ виртуальными
> Конструктор ВСЕГДА невиртуальный метод!
У Вас исходники VCL имеются? Так вот, в модуле Classes есть такой класс - TComponent называется. Очень советую посмотреть его конструктор. А в конструкторах его многочисленных потомков Вы найдете странное слово override. Что бы оно значило?
> Это как - объекта, значит, нету, да? а его VMT уже - есть???
VMT - принадлежность класса, а не объекта. Объект может быть еще и не создан, а вот его класс есть всегда, раз уж он используется в программе. И VMT, соответственно, тоже есть всегда. Как Вы думаете, что такое класс и объект с точки зрения машинного кода?
← →
Оливейра (2003-01-08 00:06) [4]Я в Delphi, признаюсь, слаб. Щас мастеров на помощь позовем :-)
Overriding a method means extending or refining it, rather than replacing it. A descendant class can override any of its inherited virtual methods.
С точки зрения С++, более академичного языка, это - абсурд:
constructor Create; virtual;
В то же время смысл есть - видимо, просто происходит вызов всех методов Create в цепочке наследования по очереди. Но это, видимо, не конструктор в классическом понимании, а скорее инициализатор. Где и когда создается VMT - интересно было бы самому узнать у Мастеров.
ИМХО, в терминах С++ конструктор Delphi это просто статическая (static, или она же class function) функция, создающая экземпляр класса в куче и возвращающая указатель на него. Хрен его знает, как там в самом деле реализовано. В С++ можно сделать аналогичный финт ушами:
class TMyClass {
private:
TMyClass() {}; // запрет создания объекта в стеке
public:
virtual ~TMyClass() {}
static TMyClass *Create() {
return new TMyClass;
}
};
...
TMyClass *pClass = new TMyClass; // фиг
TMyClass *pClass = TMyClass::Create(); // Ok
← →
Оливейра (2003-01-08 00:10) [5]Ну да... Класс - это просто инициализированный кусок памяти, а при создании экземпляра по новому указателю просто происходит нечто вроде (mem)move?
← →
Юрий Зотов (2003-01-08 00:40) [6]> Где и когда создается VMT
Компилятором. При компиляции кода класса. Вот структура VMT в Delphi 5:
–76 pointer to virtual method table (or nil)
–72 pointer to interface table (or nil)
–68 pointer to Automation information table (or nil)
–64 pointer to instance initialization table (or nil)
–60 pointer to type information table (or nil)
–56 pointer to field definition table (or nil)
–52 pointer to method definition table (or nil)
–48 pointer to dynamic method table (or nil)
–44 pointer to short string containing class name
–40 instance size in bytes
–36 pointer to a pointer to ancestor class (or nil)
–32 pointer to entry point of SafecallException method (or nil)
–28 entry point of AfterConstruction method
–24 entry point of BeforeDestruction method
–20 entry point of Dispatch method
–16 entry point of DefaultHandler method
–12 entry point of NewInstance method
–8 entry point of FreeInstance method
–4 entry point of Destroy destructor
0 entry point of first user-defined virtual method
4 entry point of second user-defined virtual method
...
Как видите, все поля VMT несут информацию о КЛАССЕ объекта, а не о самом объекте. Поэтому для построения VMT компилятору достаточно иметь исходный код класса (который у него заведомо имеется). А сам класс есть не что иное, как просто указатель на построенную компилятором VMT этого класса.
Когда конструктор вызывается, как классовый метод, компилятор вставляет в его код пролог. В нем вызывается классовый метод NewInstance, который и создает экземпляр объекта. Но и он ТОЖЕ виртуальный. И это очень удобно - замещая его, можно, например, легко сделать singletone и пр. Компилятор просто вызывает метод, адрес которого находится в данной VMT по смещению -12. Какой же тут абсурд, в чем он?
Кстати, конструктор может вызываться не только как классовый метод, но и как обычный метод объекта. Во втором случае новый экземпляр не создается, а просто выполняется код конструктора.
← →
Tsr (2003-01-08 01:24) [7]Уважаемые господа ! Очень интересен Ваш разговор, но ответьте на вопрос, пожалуйста !
Зачем вообще использовать override ?
Ведь можно просто перекрыть метод (в том числе и статический) и вызвать конструктор предка.
Пример.
TA=class
procedure SomeProcedure; dynamic
end;
...
TB=class(TA)
procedure SomeProcedure;override
end;
...
procedure TB.SomeProcedure;
begin
inherited
//Что-нибудь этакое
end;
Тут переопределение по всем правилам.
Но чем грозит такой, вроде бы аналогичый вариант ?
TA=class
procedure SomeProcedure;
end;
...
TB=class(TA)
procedure SomeProcedure;
end;
...
procedure TB.SomeProcedure;
begin
inherited
//Что-нибудь этакое
end;
?!
← →
Ihor Osov'yak (2003-01-08 01:48) [8]2 Tsr (08.01.03 01:24)
Вот более понятный пример
TFigure = class
procedure Draw; virtual; // dinamic;
procedure Move(aDelta:integer);
end;
TCircle = class(TFigure)
procedure Draw; override;
end;
procedure TFigure.Move(aDelta:integer);
begin
Position:=Position+aDelta;
Draw;
end;
....
var c:TCircle;
f:TFigure;
...
c:=TCircle.Create;
c.Move(1);
f:=TFigure.Create;
f.Move(1);
Так вот, в случае наличия virtual и иже с ним, при выполнении с.Move(1) будет вызвано TCircle.Draw. А если это "иже" убоать - будет вызвано TFigure.Draw;
f.Move всегда вызывает TFigure.Draw;
....
одним словом - читайте основы обьектного программирования..
← →
Юрий Зотов (2003-01-08 01:59) [9]Проще на примере. Пусть есть такая процедура:
procedure DoSomething(Param: TA);
begin
...
Param.SomeProcedure;
...
end;
И ее вызов:
B := TB.Create;
DoSomething(B);
Так вот, если метод SomeProcedure - виртуальный, то в процедуре DoSomething будет вызван метод класса TB - того, который был фактически ПЕРЕДАН в ее параметре. А если метод SomeProcedure - статический, то в любом случае будет вызван метод класса TA - того, который ОБЪЯВЛЕН в ее параметре.
Еще пример. Пусть класс TA имеет метод M, вызовы которого есть в методах P и Q этого же класса. Мы пишем класс TB, в котором перекрываем метод M. Если он виртуальный, то методы P и Q будут вызывать НАШ код, а если он статический, то эти методы будут вызывать код класса TA, а не наш.
← →
Ihor Osov'yak (2003-01-08 02:00) [10]2 Tsr (07.01.03 21:56)
Переотределять можно любые, не только виртуальные. Но тогда в случае, когда из метода предка будет вызываться перекрытый метод, даже если мы создавали экземпляр наследника, то будет вызван метод предка. Если же перекрытый метод сделать виртуальным - то вызовется метод наследника. Еще раз посмотрите мой пред постинг, надеюсь все понятно. То есть виртуализация позволяет из методов предка вызывать код методов наследника (хотя в момент написания и компиляции предка кода наследника могло и не быть и в помине). Полиморфизмом и поздным связыванием это дело в книжках называется.
← →
Alex Konshin (2003-01-08 02:05) [11]Ты путаешь override c hide (как это по-русски? что-то от "прятать").
Вариант не аналогичный, потому как если ты потом определишь процедуру
procedure YetAnotherProcedure( obj : TA );
begin
obj.SomeProcedure;
end;
И вызовешь ее с объектом класса TB, то в первом варианте сработает TB.SomeProcedure, а во втором TA.SomeProcedure,
чуешь теперь разницу?
Чтоб еще сильнее ее почувствовать, добавь в первом случае квалификатор abstract к описателю метода, и получишь класс, для которого не бывает объектов, но зато бывают объекты классов-наследников, и у каждого будет своя реализация этого метода. А в YetAnotherProcedure будет вызываться соответствующий метод класса-наследника.
Теперь понял?
2 Оливейра: это в С++ конструктор не может быть виртуальным, а у нас может.
Как говорил Ослик Иа: "Это другие не входят! А мой входит!"
← →
Юрий Зотов (2003-01-08 02:12) [12]> Оливейра © (08.01.03 00:10)
> Класс - это просто инициализированный кусок памяти,
Класс - это указатель на VMT. В которой, в свою очередь, содержатся (прямо, либо через другие таблицы) адреса методов этого класса. То есть, класс - это как бы совокупность кода и объявление структура данных, но не сами данные.
> а при создании экземпляра по новому указателю просто
> происходит нечто вроде (mem)move?
А при создании экземпляра создается конкретный экземпляр этих самых данных и возвращается ссылка на его начало. То есть, из VMT берется размер (по смещению -40) и вызывается что-то вроде GetMem, вот и все.
← →
Ihor Osov'yak (2003-01-08 02:14) [13]> что-то вроде GetMem, вот и все.
ну еще обнуление...
← →
Oleg_Gashev (2003-01-08 02:17) [14]>>Но чем грозит такой, вроде бы аналогичый вариант ?
>>TA=class
>> procedure SomeProcedure;
>>end;
>>...
>>TB=class(TA)
>> procedure SomeProcedure;
>>end;
>>...
>>procedure TB.SomeProcedure;
>>begin
>> inherited
>> //Что-нибудь этакое
>>end;
Есть очень хорошее свойство виртуальных методов.
Посмотрим пример:
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils;
type BaseClass=class
procedure Show();virtual;
end;
type DerriveClass=class(BaseClass)
procedure Show();override;
end;
procedure BaseClass.Show();
begin
writeln("BaseClass");
end;
procedure DerriveClass.Show();
begin
writeln("DerriveClass");
end;
var b:BaseClass;
begin
b:=DerriveClass.Create;
b.Show;
readln;
end.
b у нас переменная класса BaseClass созданна от DerriveClass и благодаря virtual, мы выкинули следующий трюк. У нас на b.Show произошел вызов метода с DerriveClass. Убрав virtual и override будет вызываться метод с BaseClass.
Удобно проводить такой трюк, когда необходимо заменить действие какого-то метода для данного класса. Переменная у нас класса BaseClass и метод Show выполняет совсем другое действие.
← →
Ihor Osov'yak (2003-01-08 02:22) [15]да нет, это не трюк. Это вполне нормальное поведение. Вызов согласно таблицы виртуальных методов, которая "привязывается" к екзампляру при "обработке" соотв. конструктора..
+ немножко игры со совместимостью типов...
← →
Ihor Osov'yak (2003-01-08 02:27) [16]> когда необходимо заменить действие какого-то метода для данного класса
Только ничего мы так не меняем. Просто наследуем и перекрываем. Но это уже придирки к терминам и их использованию..
← →
Tsr (2003-01-08 19:01) [17]1) Юрий Зотов © (08.01.03 01:59)
Еще пример. Пусть класс TA имеет метод M, вызовы которого есть в методах P и Q этого же класса. Мы пишем класс TB, в котором перекрываем метод M. Если он виртуальный, то методы P и Q будут вызывать НАШ код, а если он статический, то эти методы будут вызывать код класса TA, а не наш.
Я правильно понимаю:
TA=class
procedure M;
procedure P;
end;
procedure TA.M;
begin
showmessage("Class TA");
end;
procedure TA.P;
begin
M;
end;
...
TB=class (TA)
procedure M;
end;
procedure TB.M;
begin
showmessage("Class TB")
end;
...
var Example:TB;
BEGIN
Example:=TB.Create;
Example.P;
END.
В результате Example.P на экран будет выведено сообщение "Class TA" ?
Очень интересно.
2) А если будет так:
procedure TA.P;
begin
TA.M;
end;
Тогда даже если процедура M будет указана как виртуальная и в потомке TB перекрыта, все равно TB.P вызовет обработчик TA.M ?
3) Если есть класс TA с виртуальным методом M, есть TB - потомок TA, где метод M переопределяется директивой override, есть TC - потомок TB, где метод M еще раз переопределяется директивой override, то:
procedure TC.M; //override
begin
//какой-то код
inherited
end;
Если вызвать TC.M то выполнится какой то код, потом выполнится обработчик TB.M, потом выполнится обработчик TA.M ?
И если TA.M будет статическим методом (и в потомках не будет директивы override), то будет выполнено тоже самое ?
4) А если метод сначала статический, как TObject.Create. А потом его пишут как виртуальный, как у TComponent.Create;vurtual - то это ничего ?
5) Где можно подробно почитать про VMT ? Просто последствия понятны, но причины... Не понимаю, почему вызывается то метод наследника, то метод предка...
Вроде это может сказаться только если мудрят с приведениями типов ?
Ну в общем, хотелось бы прочитать про механизм реализации статических, виртуальных и динамических методов, чтобы лучше понять их природу
P.S. Если не сложно, ответьте на все пять вопросов !
Заранее спасибо.
← →
petr_v_a (2003-01-08 19:26) [18]> Оливейра © (07.01.03 22:43)
По поводу Вирта - во первых, он вроде как еще не в гробу :) и похоже туда не особо собирается :), во-вторых, конструкторы и пр. игрушки :) ему по барабану - он изобрел старый добрый процедурный Паскаль, которым некоторых, по-видимому, мало дрючили на первых курсах :)
← →
Оливейра (2003-01-08 21:56) [19]petr_v_a © (08.01.03 19:26)
> Оливейра © (07.01.03 22:43)
По поводу Вирта
Да Бог с ним, ошибся, с кем не бывает :-)
Итак, сутки спустя, поковыряв VCL ( System.pas ), пришел к выводу, что выделением памяти для объекта в куче и установкой указателя на VMT, чем и должен заниматься конструктор, метод Create не занимается.
Попутно обнаружил ряд иных более полезных хункций, которые и занимаются оными низкоуровневыми опреациями, например
class function TObject.InitInstance(Instance: Pointer): TObject;
К сожалению, ассемблер понимаю примерно как болгарский язык, посему...
Уважаемый Юрий Зотов!:-) Прошу вас рассказать вкратце, в двух словах, чего творится на делфевой кухне. Не хочется помереть неучем ( серьезно ). Заранее спасибо.
← →
vuk (2003-01-08 22:15) [20]Я немного не Юрий Зотов, но по поводу инициализации объектов могу сказать, что да, действительно Create выделением памяти под экземпляр явно не занимается. Компилятор автоматически вставляет в конструктор вызов _ClassCreate (это в D6), откуда вызывается метод класса NewInstance, где собственно и происходит выделение памяти и там же вызывается упомянутый Вами InitInstance для начальной инициализации экземпляра (в частности там происходит корректировка таблиц реализации интерфейсов для конкретного экземпляра). И уже после этого управление попадает в код, который явно написан в конструкторе.
← →
vuk (2003-01-08 22:24) [21]На самом деле еще нужно понимать, что в отличие от C++, в Delphi есть понятие ссылки на класс. В принципе её, в терминах Буча, можно назвать метаклассом, то есть неким специальным видом классов, экземпляры которого являются обычными классами. Тогда сразу же появляется смысл в виртуальных конструкторах. К тому же конструктор не более, чем обычный метод класса, который занимается созданием экземпляров.
← →
Tsr (2003-01-08 22:25) [22]Люди. Господа. Мастера.
Ответьте пожалуйста на пять вопросов Tsr (08.01.03 19:01)
Мне это очень, очень важно !
← →
Ihor Osov'yak (2003-01-08 22:28) [23]2 Tsr (08.01.03 19:01)
Сорри, я не Зотов, но попвтаюсь ответить, если что не так, Юрий поправит (может).. Юрию наверное будет более интересно на Оливейра © (08.01.03 21:56) внимание обоатить
Итак:
> >
> Еще пример. Пусть класс TA имеет метод M, вызовы которого
> есть в методах P и Q этого же класса. Мы пишем класс TB,
> в котором перекрываем метод M. Если он виртуальный, то методы
> P и Q будут вызывать НАШ код, а если он статический, то
> эти методы будут вызывать код класса TA, а не наш.
>
Тут у вас немного путаница. Речь можно вести об екземплярах класов... Напрямую через идентификатор класса можно вызывать только классовое методы, но речь сейчас не об этом.
Итак, немного скорректировав: если создать екземпляр от класса TA, то из его методов P и Q всегда будет вызыватся код метода M, который декларирован в классе TA, независимо от того, виртуальный метод М или нет.
Другое дело, если создать екземпляр от TB. Тогда при обращении к методам P и Q этого екземпляра будет работать код TA.M, если М не виртуальный и код TB.M, если метод M обьявлен как виртуальный (динамический)...
> Я правильно понимаю:
>
> TA=class
> procedure M;
> procedure P;
> end;
>
> procedure TA.M;
> begin
> showmessage("Class TA");
> end;
>
> procedure TA.P;
> begin
> M;
> end;
>
> ...
>
> TB=class (TA)
> procedure M;
> end;
>
> procedure TB.M;
> begin
> showmessage("Class TB")
> end;
>
> ...
>
> var Example:TB;
> BEGIN
> Example:=TB.Create;
> Example.P;
> END.
>
> В результате Example.P на экран будет выведено сообщение
> "Class TA" ?
>
> Очень интересно.
Да, если М не виртуальный. И получите "Class TB" если он виртуальный ...
Продолжение следует ...
← →
Oleg_Gashev (2003-01-08 22:29) [24]>>4) А если метод сначала статический, как TObject.Create. А потом его пишут как виртуальный, как у TComponent.Create;vurtual - то это ничего ?
Метод становится виртуальным для классов, порожденных от TComponent.
>> 5) Где можно подробно почитать про VMT ? Просто последствия понятны, но причины... Не понимаю, почему вызывается то метод наследника, то метод предка...
Вроде это может сказаться только если мудрят с приведениями типов
У Страуструпа все хорошо изложено.
← →
Oleg_Gashev (2003-01-08 22:36) [25]Все зависит от чего Вы порождаете экземпляры класса. Посмотрите еще раз мой пример (от 08.01.03 02:17). Из какого класса был создан b и к чему это привело. Почитайте о паттернах. Очень поможет для понимания.
← →
vuk (2003-01-08 22:42) [26]1. Совершенно верно.
2.
>procedure TA.P;
>begin
> TA.M;
>end;
Такой синтаксис неприменим к методам экземпляра. Так можно вызывать только методы класса.
3. Выполнится ли TA.M зависит от того, что написано в TB.M.
4. Будет существовать два метода - один виртуальный, а другой - нет. И все будет зависеть от контекста в котором происходит вызов. Например, если Ваш пример поменять так:
type
TA=class
procedure M;
procedure P;
end;
...
TB=class (TA)
procedure M; virtual;
end;
...
var Example:TB;
BEGIN
Example:=TB.Create;
Example.M;
TA(Example).M;
END.
то выведено будет:
Class TB
Class TA
← →
Ihor Osov'yak (2003-01-08 22:48) [27]
2 Tsr (08.01.03 19:01)
Да, в пред постинге предполагается, что при обьявлении virtual не забывается про override в наследнике. Если забыть - то будет то, что называется переопредиление. Об этом компилятор сделает предупреждение. Тогда все же будет зваться метод метод класса А.
Продолжение
2) А если будет так:
procedure TA.P;
begin
TA.M;
end;
Тогда даже если процедура M будет указана как виртуальная и в потомке TB перекрыта, все равно TB.P вызовет обработчик TA.M ?
Во первых, компилятор обругает. Такой вариант возможно проходил в досовском паскале, за древностью уже не помню.
если же написать
self.M;
или
TA(self).M;
то это ничего не изменит.
См. особенности вызовов виртуальных через таблицук виртуальных методов, или ждите, пока кто здесь чего напишет ... Может я позже, если терпения хватит..
2 Oleg_Gashev - привет, получил ночью мое письмо, вернее два? Или это мой пров глючит? сорри за офтопик - но это лишь две строчки...
← →
Oleg_Gashev (2003-01-08 22:51) [28]Offtopic
>> Ihor Osov"yak
ICQ
← →
Oleg_Gashev (2003-01-08 22:54) [29]>> См. особенности вызовов виртуальных через таблицук виртуальных методов, или ждите, пока кто здесь чего напишет ...
А что здесь писать? Читай полиморфизм.
← →
Ihor Osov'yak (2003-01-08 23:07) [30]в дополнение vuk © (08.01.03 22:42)
по п4
если делать так:
TA=class
procedure M;
procedure P;
end;
procedure TA.M;
begin
showmessage("Class TA");
end;
procedure TA.P;
begin
M;
end;
type
TB=class (TA)
procedure M; virtual;
end;
procedure TB.M;
begin
showmessage("Class TB")
end;
var Example:TB;
BEGIN
Example:=TB.Create;
Example.P;
END.
то мы снова имеем то, что называется переопредиление..
TB.M и TA.M ничего общено не имеют. И сообщение будет соотв. Class TA. Вообще, строго говоря, переопредиление здесь никакой роли не играет. Главное то, что TA.M - невиртуальный и при компиляции TA.P вызов M будет зашит жестко. И хотя мы будем звать
Example.P, но посколько реально будет работать TA.P, то он вызовет то, что жестко зашито, то есть метод M класса A.
Коротко - вызов виртуальных - через таблицу виртуальных. А что там будет прописано - уже зависит что и как переопределяется. Вызов невиртуальных - жесткый, адресс вызова определяется на этапе компиляции.
← →
Oleg_Gashev (2003-01-08 23:11) [31]Очень часто класс, содержащей виртуальный метод называют полиморфным классом. Самое главное отличие заключается в том, что полиморфные классы допускают обработку объектов, тип которых неизвестен во время компиляции. Функции, описанные в базовом классе как виртуальные, могут быть модифицированы в производных классах, причем связывание произойдет не на этапе компиляции (то, что называется ранним связыванием), а в момент обращения к данному методу (позднее связывание).
← →
Tsr (2003-01-09 19:40) [32]>>У Страуструпа все хорошо изложено.
А кто это ? У него книга по Дельфи есть хорошая ?
А в интернете ничего подобного нету ? Вроде где-то видел нечто подобное, подробное описание виртуальных, статических методов... но не помню где
сейчас ищу - ну могу найти...
← →
Suntechnic (2003-01-09 20:29) [33]Oleg_Gashev © (08.01.03 23:11)
Очень часто класс, содержащей виртуальный метод называют полиморфным классом.
Не часто, а всегда. Это есть определение полиморфного класса.
← →
Suntechnic (2003-01-09 20:32) [34]Tsr (09.01.03 19:40)
>>У Страуструпа все хорошо изложено.
А кто это ? У него книга по Дельфи есть хорошая ?
Таких людей как Страуструп надо знать ну хотя бы не в лицо, так по имени :). Он создатель языка С++ и по Делфи книжки никогда не писал и писать не будет. Тем не менее в его книгах про ООП очень много чего полезного подчерпнуть можно, только ИХМО не стоит вам за него сейчас браться.
← →
Оливейра (2003-01-09 20:52) [35]Чего-то Юрий Зотов пропал (тысячу извинений :)...
С ума сойти. Дайте ссылку, что ли, пожалуйста, на то, как на самом деле осуществляется конструирование (второй день подряд спать лечь вовремя не могу... навязчивая идея...) в Object Pascal.
Впрочем, щас топик новый заведу, пожалуй, а?
← →
Юрий Зотов (2003-01-09 22:12) [36]> Оливейра © (09.01.03 20:52)
Эх, народ, тут у меня такое в жизни творится... не до форумов.
См. здесь:
http://www.delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1042134867&n=3
← →
Tsr (2003-01-09 23:00) [37]Юрий Зотов, а что случилось ? Может чем помочь можно ?
Или событие то хоть радостное ?
Извините, если задеваю личное...
P.S. А никто ссылку то на статью какую по мтодам не знает ?
← →
Tsr (2003-01-11 10:19) [38]А никто ссылку то на статью какую по методам не знает ?
← →
Oleg_Gashev (2003-01-11 11:19) [39]http://www.firststeps.ru/theory/oop/oop1.html
← →
Tsr (2003-01-11 12:02) [40]А можно ссылочку, где по Object Pascal ?
← →
Сатир (2003-01-11 15:30) [41]вообщем, вставлю и я свои "5 копеек"
override - значит, обнулять, аннулировать
и если стоит этот спецификатор, то все действия, кторые были описаны в предке, будут обнулены.
Но если всё-таки возникнет необходимость их вызвать в наследнике, то воизбежание перенабирания кода, который есть в предке, используется директива inherited
а если этой директивы не будет, то Вы получите следующую ошибку:
Unsatisfied forward or external declaration: "<имя метода, который был переобъявлен в наследнике>"
← →
Игорь Шевченко (2003-01-11 15:50) [42]Сатир © (11.01.03 15:30)
> override - значит, обнулять, аннулировать
Где такая трава растет, поделись ?
override всю жизнь был "Перекрывать"
← →
Сатир (2003-01-11 15:57) [43]2Игорь Шевченко © (11.01.03 15:50)
главное смысл
а если хотите, чтоб и Вас так заколбасило, переведите сабж с помощью Лингво 7 или Сократ 97, там, правда, мне сказали:"аннулируйтесь".
Гы.
← →
Игорь Шевченко (2003-01-11 16:03) [44]Сатир © (11.01.03 15:57)
Смысл напрочь искажен.
Впрочем, тому же Stylus"у были скормлены строчки:
Daddy"s flown across the ocean
Leaving just a memory
A snapshot in the family album...
И гарантировано полчаса здорового смеха :-))
← →
Tsr (2003-01-11 16:06) [45]Где можно подробно почитать про VMT ?
хотелось бы прочитать про механизм реализации статических, виртуальных и динамических методов, чтобы лучше понять их природу
← →
Игорь Шевченко (2003-01-11 16:13) [46]Tsr (11.01.03 16:06)
Тейксейра, Пачеко: "Delphi 5. Руководство разработчика"
Object pascal language guide.
Исходные тексты Source\Rtl\Sys\system.pas
Отладчик.
Удачи!
← →
Tsr (2003-01-11 16:55) [47]Игорь Шевченко, а в книге этих авторов по D5 это есть ? У меня книга по D6 - там как-то особо не описано...
← →
Игорь Шевченко (2003-01-11 17:13) [48]Tsr (11.01.03 16:55)
К книге по D6 на компакте прилагается книга по D5. Есть.
← →
vuk (2003-01-11 17:54) [49]to Игорь Шевченко:
>Где такая трава растет, поделись ?
Какая ж зимой трава? Зимой - грибы! :o)
to Tsr:
>Где можно подробно почитать про VMT ?
Попробовать вкратце объяснить что ли?
Для начала про статические методы (не путать со static в C++) т.к. они самые простые - когда делается взов статического метода, компилятор генерирует обычные вызов процедуры по определенному адресу.
Теперь про виртуальные. При помощи виртуальных методов реализуется одно из наиболее важных средств ООП - полиморфизм. Для этого компилятор генерирует таблицы (VMT), в которые помещаются адреса виртуальных методов объекта а каждый экземпляр любого класа содержит ссылку на VMT своего класса. При вызове виртуального метода происходит не прямой вызов метода, а сначала берется адрес VMT класса, затем из нее извлекается адрес метода и уже потом делается вызов по этому адресу (косвенный вызов).
Предположим, что есть класс определяющий 3 виртуальных метода:
TBaseClass = class
procedure Foo; virtual;
procedure Bar; virtual;
end;
тогда какой-то фрагмент VMT (для упрощения буду рассматривать только фрагмент, а не всю VMT, поскольку для понимания того, как это работает не важно - рассматривается VMT целиком или только её фрагмент) схематично будет выглядеть так:
...
[@TBaseClass.Foo]
[@TBaseClass.Bar]
...
Когда где-то в коде написан вызов виртуального метода, например Foo, компилятор сгенерирует код, который извлекает из определенной ячейки VMT нужный адрес (TBaseClass.Foo) и потом происходит вызов по этому адресу.
Теперь предположим, что мы определили наследника от TVaseClass:
TSomeNewClass = class(TBaseClass)
procedure Foo; override;
end;
Для этого класса компилятор сгенерирует VMT, где интересующий нас фрагмент будет выглядеть так:
...
[@TSomeNewClass.Foo]
[@TBaseClass.Bar]
...
То есть структора VMT наследника поврторяет структуру VMT предка, и в ней замещены адреса, соответствующие переопределенным методам. Теперь, когда будет происходить вызов метода Foo, из VMT будет извлечен адрес метода TSomeNewClass.Foo.
Теперь немного про динамические. В больших программных системах со сложными иерархиями и большим количеством виртуальных методов возникают классы, у которых имеются очень большие VMT, где методы переопределяются редко и это приводит к потерям памяти на хранение этих таблиц. Вот как раз эту проблему и решают введением динамических методов.
По сути своей динамические методы не сильно отличаются от виртуальных, но есть некоторые отличия.
Для этого применяются следующие меры:
1. У каждого динамического метода есть неявно назначаемый компилятором индекс.
2. Каждый элемент таблицы динамических методов (DMT) содержит, помимо адреса метода, также его индекс и хранится отдельно от VMT.
3. DMT для класса содержит только адреса только тех методов, что были переопределены либо заново введены в этом классе.
Схематический пример построения структуры DMT.
Есть классы:
TBaseClass = class
procedure Foo; dynamic;
procedure Bar; dynamic;
end;
TSomeNewClass = class(TBaseClass)
procedure Foo; override;
end;
Пусть компилятор присвоил Foo индекс 1, а Bar - 2.
Тогда структуры DMT будут следующие (схемстично):
Для TBaseClass
...
[1; @TBaseClass.Foo]
[2; @TBaseClass.Bar]
...
Для TSomeNewClass
...
[1; @TSomeNewClass.Foo]
...
При этом у каждого экземпляра объекта в VMT есть ссылка на DMT для этого класса. Вызов динамического метода происходит так:
1. Получается адрес DMT класса.
2. В DMT ищется адрес метода с нужным индексом.
3. Если адрес найден, то делается вызов, иначе же поиск производится последовательно в DMT классов-предков.
В приведенном выше примере при вызове Bar у экземпляра TSomeNewClass сначала будет произведено сканирование DMT TSomeNewClass, но поскольку метод с нужным индексом там не найден, будет просмотрена DMT TBaseClass и вызван метод TBaseClass.Bar.
Добавлю еще, что методы с модификатором message, это по сути те же динамические методы и располагаются они в тех же самых DMT, но для них явно назначается индекс для DMT и этим индексом является код сообщения Windows.
Страницы: 1 2 вся ветка
Форум: "Потрепаться";
Текущий архив: 2003.01.27;
Скачать: [xml.tar.bz2];
Память: 0.64 MB
Время: 0.01 c