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

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

Почему нет ошибки? [D7]


Страницы:   1 2

мскл ©   (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


Страницы:   1 2

Наверх

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