![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Почему нет ошибки? [D7]
мскл © (2008-02-06 12:38:00)Разбираюсь с классами. Уровень начинающий. Возник вопрос, есть такой код:
procedure TForm1.Button1Click(Sender: TObject);
var
b1, b2, b3: TButton;
begin
b1.Free;
b1 := TButton.Create(Self);
b3 := TButton.Create(Self);
ShowMessage('b1 = 0x' + IntToHex(Integer(b1), 8));
b2 := b1;
ShowMessage('b2 = 0x' + IntToHex(Integer(b2), 8));
ShowMessage('b3 = 0x' + IntToHex(Integer(b3), 8));
end;
Почему в таком случае не возникает ошибки на выделенном месте: я же пытаюсь уничтожить ещё не созданный объект?
И ещё вопрос про такой фокус - не создаем b3, оставшийся код без изменений, получаем "Abstract error":
var
b1, b2, b3: TButton;
begin
b1.Free;
b1 := TButton.Create(Self);
// b3 := TButton.Create(Self);
ShowMessage('b1 = 0x' + IntToHex(Integer(b1), 8));
b2 := b1;
ShowMessage('b2 = 0x' + IntToHex(Integer(b2), 8));
ShowMessage('b3 = 0x' + IntToHex(Integer(b3), 8));
Потом убираем из модифициорванного кода уничтожение не созданного b1, исчезает ошибка:
var
b1, b2, b3: TButton;
begin
// b1.Free;
b1 := TButton.Create(Self);
// b3 := TButton.Create(Self);
ShowMessage('b1 = 0x' + IntToHex(Integer(b1), 8));
b2 := b1;
ShowMessage('b2 = 0x' + IntToHex(Integer(b2), 8));
ShowMessage('b3 = 0x' + IntToHex(Integer(b3), 8));
Как это называется?
Ins (2008-02-06 14:10:00) [1]
> call TObject.Free
Но таким образом вызываются статические методы. Т.е. для определения его адреса self не нужен, адрес известен на этапе компиляции. С виртуальными же методами ситуация совсем другая. Чтобы узнать его адрес, в рантайм идет обращение к Self.
oxffff (2008-02-06 13:56:00) [2]
> Ins © (06.02.08 13:52) [20]
offtop,
Спасибо.
Можно как-то не через ICQ.
А то для меня увы это темный лес. :)
Напиши на oxffff@yandex.ru свой телефон или контакт и я тебе перезвоню.
Заодно и познакомимся.
Ins (2008-02-06 13:52:00) [3]
> oxffff © (06.02.08 13:50) [18]
Сорри за offtop, oxffff, если работа все еще нужна, свяжись со мной по ICQ: 371-759-799
Ins (2008-02-06 13:51:00) [4]
> А откуда тогда вызываются статические методы?
Код метод TObject.Free содержится в единственном экземпляре и его адрес компилятору известен. Соответственно вызов этого метода выглядит так:
mov eax, b1 // Передаем в параметры Self
call TObject.Free
Все. В этих двух строчках кода исключение не возникнет. А вот внутри TObject.Free - возможно, так как Self невалиден.
oxffff (2008-02-06 13:50:00) [5]
> мскл (06.02.08 13:45) [17]
compile time binding.
мскл (2008-02-06 13:45:00) [6]
> Чего? При объявлении объекта в var ему выделяется 4 байта
> в стеке для ссылки на экземпляр.
А откуда тогда вызываются статические методы? Ведь память под объект не выделена (= nil).
Как тогда происходт вызов Obj.Free - точнее КУДА он происходит? Obj = nil
oxffff (2008-02-06 13:42:00) [7]
> мскл (06.02.08 13:36) [13]
> Спасибо. Т.е. получается, что при объявлении объекта в var
> ему все же выделяется какая-то память (автоматически) для
> выполнения всех его статических методов без инициализации
> по конструктору?
Выделяется слот стека для указателя на объект.
И поскольку экземпляр классового типа не является managed (не в понятии & и o .NET естественно), то для типа не вызывается инициализатор.
Ins (2008-02-06 13:41:00) [8]Вы что, думаете что адреса методов хранятся внутри экземпляра?
Ins (2008-02-06 13:40:00) [9]
> Спасибо. Т.е. получается, что при объявлении объекта в var
> ему все же выделяется какая-то память (автоматически) для
> выполнения всех его статических методов без инициализации
> по конструктору?
Чего? При объявлении объекта в var ему выделяется 4 байта в стеке для ссылки на экземпляр. b1 - это всего лишь указатель на объект в динамической памяти.
мскл (2008-02-06 13:36:00) [10]Спасибо. Т.е. получается, что при объявлении объекта в var ему все же выделяется какая-то память (автоматически) для выполнения всех его статических методов без инициализации по конструктору?
Ins (2008-02-06 13:29:00) [11]
> Если он не равен nil, то вызывается виртуальный Destroy
Ой, я хотел выделить слово виртуальный.
Ins (2008-02-06 13:27:00) [12]Происходит следующее:
1. В метод неявно передается b1, внутри метода он будет рассматриваться как параметр Self
2. Вызывается статическийметод Free, его адрес известен на этапе компиляции, так что сам факт вызова метода даже с невалидным Self не приведет ни к чему катастрофическому.
3. Внутри метода анализируется Self.
3.1. Если он равен nil, то просто выходим из метода.
3.2. Если он не равен nil, то вызывается виртуальный Destroy. Виртуальный - означает, что его адрес не известен на этапе компиляции и вычисляется в рантайм. Для этого нужно обратиться к Self, прочесть из него адрес VMT и найти в VMT нужный метод по известному смещению. Вот тут и начинается самое интересное - мы читаем из непонятной области памяти непонятное число, считаем что это указатель на VMT. Потом еще читаем из адреса, которое содержит это число + смещение. Потом что получилось пытаемся вызвать. Так как эти адреса на самом деле не имеют смысла - произойдет все что угодно - может AV, может - Abstract Error, может - форматирование жесткого диска. Как повезет :)
oxffff (2008-02-06 13:21:00) [13]
> Мой пример с уничтожением до создания - не корректен ведь?
ДА, не корректен.
> Из этого делаю вывод, что вызывать эту процедуру у неинициализированного
> объекта нелья. Я прав?
НЕТ. Виртуальный метод нельзя. Статический метод можно.
> А если nil, то тогда что, что в таком случае происходит
> с b1.Free?
clickmaker © (06.02.08 12:49) [3]
мскл (2008-02-06 13:14:00) [14]Затеял я это случайно, хотел посмотреть "в живую" как создаются, "копируются" ссылки на экземпляры класса.
Спасибо. Кажется я стал понимать. Но все же задам ещё пару вопросов:
Мой пример с уничтожением до создания - не корректен ведь?
b1.Free;
Я знаю что Free объявлен как простаяprocedure Free;в TObject. Из этого делаю вывод, что вызывать эту процедуру у неинициализированного объекта нелья. Я прав?
И т.е. получается вся ситуация возникает из-за того, что локальная переменная b1 не факт что изначально является nil? Но вот в этом месте все равно не понимаю - ну если она не nil - тогда b1.Free пытается где-то из грязного адреса b1 отыскать Free, получается тогда "Abstract Error". А если nil, то тогда что, что в таком случае происходит с b1.Free?
Ну и конечно ещё раз спасибо. И извинения за бред, если это так.
Anatoly Podgoretsky (2008-02-06 13:06:00) [15]А плакать потом будем.
clickmaker (2008-02-06 12:55:00) [16]
> [6] Leonid Troyanovsky © (06.02.08 12:54)
ну повезло человеку ) порадуемся
Leonid Troyanovsky (2008-02-06 12:54:00) [17]
> ANB (06.02.08 12:49) [2]
> clickmaker © (06.02.08 12:49) [3]
А никто не обещал nil для локальных переменных.
Т.е., [4].
--
Regards, LVT.
Ins (2008-02-06 12:53:00) [18]Учитывая, что b1 - локальная переменная, ее значение без инициализации содержит мусор из стека. Этот мусор может содержать ноль - тогда все пройдет гладко, так как [2], [3], а может не ноль - тогда можно ожидать чего угодно, на что только фантизии хватит :)
oxffff (2008-02-06 12:52:00) [19]
> Как это называется?
Это называется использование неинициализированных данных,
а проще говоря грязных слотов стека.
clickmaker (2008-02-06 12:49:00) [20]
> Почему в таком случае не возникает ошибки на выделенном
> месте: я же пытаюсь уничтожить ещё не созданный объект?
To destroy an object, however, you should call the Free method (also inherited from TObject), because Free checks for a nil reference before calling Destroy.
(c) Delphi Help