delphi just die when compile this code =)

Тема в разделе "WASM.HEAP", создана пользователем S_T_A_S_, 24 июн 2005.

  1. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    S_T_A_S_

    По сообщению с delphimaster версия 17 уже не содержит этого бага.

    Насчет области видимости - в Delphi это понятие ограничивается в пределах функций, а поскольку регулярно используется полиморфизм и наследование - предпочитаются явные вызовы конструктора и защищеные деструктора:
    Код (Text):
    1.  
    2. procedure x;
    3. var s: TStream; // class with abstract methods
    4. begin
    5.   s := TFileStream.Create ('file.bin')
    6.   someobj1.SaveToStream (s);
    7.   someobj2.SaveToStream (s);  
    8.   s.Free;
    9. end;
    10.  


    В C++ пришлось бы реализовать через приведение типов, но обойтись без вызова деструктора:
    Код (Text):
    1.  
    2. void x ()
    3. {
    4.    TFileStream  s ("file.bin");
    5.    TStream &ss = *(TStream*) &s;
    6.    someobj.SaveToStream (ss);
    7.    someobj.SaveToStream (ss);  
    8. }
    9.  
     
  2. volodya

    volodya wasm.ru

    Публикаций:
    0
    Регистрация:
    22 апр 2003
    Сообщения:
    1.169


    TStream &ss = *(TStream*) &s;





    мать моя :))))
     
  3. _staier

    _staier New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2003
    Сообщения:
    738
    Адрес:
    Ukraine
    S_T_A_S_

    >>Что значит при завершении программы?



    это значит , когда программа завершается:derisive:



    >>Это должно быть верно только для статических

    >>экземпляров. Если у меня локальный (автоматический)

    >>экземпляр, то деструктор разумно вызывать при завершении >>жизни объекта (при выходе их функции).

    >>Если деструктор для такого объекта будет вызываеться

    >>(неявно) при завершении - это баг гораздо более

    >>серьёзный, чем то что в этом топе :derisive:



    ничего не понимаю, то должен вызываться, то не должен



    автоматических обьектов в дельфи нет (ссылки, фактически), если иметь в виду CLASS ? если же иметь в виду object, то да имеетеся такая возможность но



    Object types are supported for backward compatibility only. Their use is not recommended.
     
  4. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    staier >




    Автоматические переменные - это переменные в стэке. В дельфи что, все переменные являются статическими и хранятся в секции данных? Конечно, если конструктор\деструктор нужно вызывать явно, то слово "автоматический" выглядит довольно странно :derisive:



    >




    Если мне не нужно наследование от какого-то там TObject? Дельфи всё же не smalltalk.



    >




    Вот гипотетический код на С++
    Код (Text):
    1. class foo{
    2.     foo(int a);
    3.     ~foo();
    4.     int get();
    5. };
    6.  
    7. int bar(int a)
    8. {
    9.     foo b(a);
    10.     int c = b.get();
    11.     return c ? bar(a) : 0;
    12. }
    Что будет, если ~foo() будет вызываться при завершении программы, а не при выходе их ф-ции, как сейчас

    ?
     
  5. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    [offtopic]

    странно, что тема ещё здесь.. :)
     
  6. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    volodya

    C++ не позволяет прямого приведения классов:

    CScrollView &valias = (CScrollView)pWnd->GetActiveView (); // cause C2259 error

    Но с указателями дружит:

    CScrollView &valias = *(CScrollView*) pWnd->GetActiveView ();



    S_T_A_S_

    1. От части отсутсутствие неявных вызовов конструктора/деструктора компенсируется назначением ответственных за это дело обьектов владельцев. Например освобождать за каждым компонентом добавляемым на форму память, явно не нужно - это произойдет при уничтожении формы.



    2. TObject - не такой уж и тяжелый класс, но если очень хочется обойтись без него - Delphi ничем помочь не сможет, разве что на ассемблере написать класс целиком. Это ведь все равно что обижаться на присутствие IUnknow в COM :)



    3. Возможен Stack corrupt, другое дело я не представляю как компилятор откомпилирует подобный код, ведь не всегда на этапе компиляции ясно, сколько раз вызывается та или иная функция. На деле в Си++ деструкторы вызываются по выходу из области видимости (как правило перед тем как будет изменен ESP).
     
  7. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    S_T_A_S_

    По моему ИМХО, в дельфях автоматически инициализируются и финализируются только предопределенные типы: динамические строки и массивы, варианты и интерфейсы (может еще че наворочали - не знаю), в том числе и входящие в состав "статических" записей и объектов. "Статических" - здесь означает, что память под них выделяется компилятором либо в секции данных (глобальные переменные) либо в стеке (локальные переменные).

    На выходе из процедуры компилятор вставляет finalize для тех локальных статичеких переменных, которые он "видит ", т.е. которые сами являются указанными предопределенными типами или содержат в себе эти типы. Например, если статическая локальная переменная типа foo = object содержит динамические строки, то при выходе из процедуры эти строки будут автоматически "очищены". Но никакие деструкторы foo при этом автоматически ИМХО не вызываются (грубо говоря, компилятор отвечает только за свои ресурсы, а о "наших" мы должны позаботиться сами). Поэтому, если деструктор foo должен делать что-то еще (например освобождать память, распределенную New или GetMem), то его нужно вызывать явно.

    Тоже относится и к динамическим переменным типа foo = object или Tfoo = class. Локальные динамич.переменные этих типов в процедуре компилятором и не инициализируются и не освобождаются - это задача программера - не забыть вызвать конструктор и деструктор. "Хорошо" это или "плохо" - дело вкуса и привычки (в асме практически все приходится делать ручками и ничего - многим нравится ;)
     
  8. _staier

    _staier New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2003
    Сообщения:
    738
    Адрес:
    Ukraine
    S_T_A_S_

    когда я коворю, что в дельфи нет автоматических обьектов , я знаю что говорю и имею в виду именно это ....





    просто замени то что ты называешь словом обьект

    на словосочетание указатель на обьект и всё станет ясно



    сонструкция var p:Tmyclass эквивалентна не tmyclass p на с++

    а tmyclass * p ;





    и посему



    разница сводится к написанию в создании динамичекого обьекта слова create вместо new





    p= new tmyclass(k1,k2,k3); //с++



    на p:=tmyclass.create(k1,k2,k3); //delphi



    а



    delete p;//c++

    на p.free;/delphi



    всё , больше разницы никакой;





    дело в том , что дельфи ушёл в сторону ооя дальше чем

    с++,(это не преимущество а просто факта) и соответственно любой обьект наследован от Tobject (как в smalltalk)и в каждом обьекте есть vmt, именно поэтому нет автоматических обьектов в которых не возможны виртуальные вызовы ...
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    А о чем собс-но речь ? Если об автоматической инициализации и удалении, то - обломс. Если о том, что в дельфи экземпляры класса обязательно должны быть динамическими, то это не совсем так. Они по умолчанию динамические, т.к. в методах InitInstance и FreeInstance используются GetMem и FreeMem. Но ежели их переопределить, то можно ИМХО и статические (глобальные или локальные) экземпляры использовать.
     
  10. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    leo

    В моей программе например, класс использует для динамической аллокации, проекцию файла, что позволяет использовать его в нескольких процессах (см. файл ChShare.pas). Чтобы сделать статический класс нужно узнать размер его экземпляра, что к сожалению невозможно на этапе компиляции (но можно узнать во время выполнения, и записать его как константу).
     
  11. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    leo >




    Да? А я всё время думал, что память выделенную new нужно освобождать оператором delete, и этот оператор САМ вызовет деструктор автоматически (т.е. неявно).



    >




    Чем жетогда эти методу отличаются от обычным? Названием? Ну это дешёвый понт :derisive:



    >




    В асме это элементарно реализуется макросами. Это же не такой ограниченный язык =)





    staier >




    Как можно заменить экземпляр объекта указателем на него? Ту не нужно ничего путать, указатель сам место занимает, и объект в этом случае нажодится в хипе, а не в стэке. Если время жизни и видимость экземпляра не превышает пределы функции, то аллоцировать такой обект в стэке не только клупо, но и опасно. Это даже не IMHO :derisive:



    >




    Что мешает автоматическому объекту иметь VMT? Ограниченносить компилятора или языка?





    alpet >




    Это очень странно. Количество member'ов, виртуальных ф-ций и размер полей изветсны, так что вычислить общий размер ничего не должно быть сложно.
     
  12. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    S_T_A_S_





    В том то и проблема, что операторы new и delete в Delphi отсутствуют. В замен есть конструктор и деструктор, которые отличаются от обычных методов прежде всего вызовами упомянутых механизмов аллокации/деалокации (ну и наверное настройкой VMT).



    То есть при вызове TSomeObject.Create, еще неявно вызываются методы NewInstance и InitInstance, которые по умолчанию работают с кучей и собственно выполняют роль перегруженного оператора new.





    В том то и дело, что дефакто размер экземпляра известен, а получать его приходится через функцию класса XClass.InstanceSize. Почему ее не смогли делать design-time аля sizeof не ясно. Впрочем я незнаю как аналогично узнается размер у обьектов C++.
     
  13. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Гм. Да, в самом деле, оказывется new/dispose поддерживаются только в free pascal :/
     
  14. alpet

    alpet Александр

    Публикаций:
    0
    Регистрация:
    21 сен 2004
    Сообщения:
    1.221
    Адрес:
    Russia
    S_T_A_S_





    Они поддерживаются и в Delphi, только не в плане создания обьектов, а например для аллокации/деалокации данных размер которых компилятору известен:
    Код (Text):
    1.  
    2. // Не очень красивый синтаксис описания обьединений (UNION)
    3. type xUnion64 = Record
    4.    case byte of
    5.     0:(lo, hi: dword);
    6.     1:(packed: Int64);
    7.    end;
    8.     xList = array [1..100] of xUnion64;
    9.     pxList = ^xList;
    10. var a: pxList;
    11. begin
    12.  New (a);
    13.  ....
    14.  Dispose (a);
    15. end;
    16.  
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Не стоит путать "старую" и "новую" реализацию ООП в борландовских паскалях.

    В старой реализации (foo = object) конструктор\деструктор были обычными процедурами (что-то типа хинта для программера) и динамические объекты создавались и удалялись путем явного указания этих процедур:
    Код (Text):
    1. var myfoo:^foo; begin New(myfoo,Create); Dispose(myfoo,Destroy); end; //это и сейчас должно работать
    Соответственно при использовании статического объекта myfoo:foo мы ес-но никаких New\Delete не вызываем и ес-но при необходимости освобождения ресурсов вызываем явно myfoo.Destroy (чем тут могут помочь асмовские макросы - я чей-то не догоняю - что Delete, что Destroy, что макрос - разницы не вижу :)

    Также ес-но если в объекте не определено ни одного виртуального метода, то у него не будет никакой VMT. Использовать такие объекты и сейчас никто не запрещает.



    Соответсвенно в "современной" реализации object pascal конструктор и деструктор имеют "особый" смысл (как и в C). Но тут борланды пошли своим путем и вместо того, чтобы сохранить New и Dispose заменили их на вызов myfoo:=foo.Create и myfoo.Free. Возможно для "любителей" Цэ это непривычно и "дико", но тут ничего не поделаешь ;)Также понятно, что любой класс как наследник TObject имеет VMT.



    Поэтому, если хочется без VMT или хочется "статики", то единственная возможность в дельфях - это использовать "устаревший" тип object.
     
  16. _staier

    _staier New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2003
    Сообщения:
    738
    Адрес:
    Ukraine
    S_T_A_S_

    "Как можно заменить экземпляр объекта указателем на него?"



    у вас слово заменяет суть



    в дельфи невозможно создать экземпляр класса иначе как динамически ( по умолчанию в хипе )



    type T= class //описание класса

    end;



    var p:T;//обьявление указателья на обьект класса Т



    p:= t.create;// создание динамического обьекта и присвоение p его адреса в памяти





    так что автоматического экземпляра класса в дельфи нет и

    быть не может





    в этом(среди прочего) и разница между обьектами object и class





















    "указатель сам место занимает, и объект в этом случае нажодится в хипе, а не в стэке."



    абсолютно верно



    "Если время жизни и видимость экземпляра не превышает пределы функции, то аллоцировать такой обект в стэке не только клупо, но и опасно. Это даже не IMHO :derisive:"



    да, это не имхо, это бред :derisive:
     
  17. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    leo >




    Мне, к счастью, не приходилось работать ни с одной версией borland pascal. Всё, что мне нужно было сейчас - скомпилировать код, который пректрасно понимает free pascal другим (глючным) компилятором :derisive:



    >




    delete освобождает выделенную (из хипа) память, а Destroy (имеется ввиду деструктор ?) производит операции завершения (их может не быть совсем).



    >




    А это каким образом связано с деструкторами?



    >




    Речь идёт не о привычке, а о принципиальном различае этих ф-ций.



    >




    Если вернуться к началу топа, то видно, что мой класс не имеет VMT, и ни от чего не наследуется.



    >




    А я его и использую, хотя меня пытаются убедить, что это не правильно.

    BTW, с точки зрения Вирта, class тоже устарел уже давно =)





    staier >




    А можно более простое объяснение, для недалёких вроде меня ? :) Или экземпляр и указатель на него суть одно и тоже?



    >




    А где доказательства ? =)

    Предлагаю посмотреть, как это можно сделать на первой странице :derisive:



    >




    А не поумолчанию, где может быть ещё выделена память под динамический экземпляр класса?



    >




    А у вас одно слово подменяет другое. Я нигде не упоминал слово "class", а употреблял "класс" и "Object" как синонимы =)



    >




    Что бред? Если ты считаешь, что можно выделить память в стэке, сохранить на неё где-то указатель, потом освободить стэковый фрейм и продолжать использовать указатель в другой ф-ции, то я порекоминдую подумать головой, и поучиться азам. Это конечно, IMHO :derisive:
     
  18. Max

    Max Member

    Публикаций:
    0
    Регистрация:
    22 май 2003
    Сообщения:
    192
    уже пару суток читаю этот флейм, и все сдерживаюсь, чтобы не ответить.

    не удержался :)))



    начиная с Turbo, а позже Borland Pascal объекты можно было объявлять только через object.

    По сути, это и есть статическое объявление объекта.

    Напр., есть объект
    Код (Text):
    1.  
    2.   PMyObject = ^TMyObject;
    3.   TMyObject = object
    4.     Field: integer;
    5.     procedure DoSomething;
    6.   end;
    7.  


    Фактически, это эквиалентно описанию
    Код (Text):
    1.  
    2.   PMyObject = ^TMyObject;
    3.   TMyObject = [packed] record
    4.     Field1: integer;
    5.   end;
    6.   procedure DoSomething(obj: PMyObject);
    7.  




    объявление obj: TMyObject приводит к выделению 4 байт на стеке или в секции данных (sizeof(Field1)).

    Для динамического создания объектов, использовались конструкции типа
    Код (Text):
    1.  
    2.   var
    3.     obj: PMyObject;
    4.   ...
    5.     New(obj);
    6.  




    Начиная с Делфи, Борланд забил на статическое объявление объектов, введи понятие class, а попутно и понятие конструктора и деструктора, видимо, чтобы унифицировать процедуры инициализации и с-но уничтожения объектов, т.к. раньше, для объектов, заводились специальные процедуры типа procedure Init;



    основная сложность заключалась в том, что надо было не забыть вызвать эту процедуру.

    Процедуру инициализации/уничтожения большая часть населения Земли обзывала кто как - хорошо, если Init/Done, а то и че похлеще.

    С ключевыми словами constructor/destructor все стало намного проще.



    Короче говоря, приведенный в самом начале топика пример некорректен - для object'а понятие конструктора/деструктора не существует, о чем недвусмысленно говорит делфевский хэлп: Since object types do not descend from TObject, they provide no built-in constructors, destructors, or other methods. You can create instances of an object type using the New procedure and destroy them with the Dispose procedure, or you can simply declare variables of an object type, just as you would with records
     
  19. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Max >




    А ведь это 2 большие разницы. Если память на стэке, то такую переменную (в том числе и экземпляр класса) принято (в С++) называть автоматической. Если в секции данных - то статической.



    >




    Это противоречит написанному в описании Turbo Pascal 6.0
     
  20. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Max

    > "для object'а понятие конструктора/деструктора не существует"

    Полноценного понятия м.б. и не существует, но как я уже говорил ключевые слова constructor и destructor существуют как хинты и только их можно использовать в New и Dispose. Например Dispose(myfoo,Done) прокатит если Done объявлен как destructor и не прокатит если как обычная procedure.



    S_T_A_S_

    Такое впечатление, что ты невнимательно читаешь и выхватываешь отдельные фразы, чтобы "поворчать" ;)



    Сначала о "главном". Твой исходный код верен за исключением одного - в паскале (классическом) скобки для пустого списка аргументов не пишут - напиши просто "destructor kill;" и наслаждайся ;)

    Дополнительно нужно иметь ввиду следующее. Никакой полной "автоматизации" в дельфи нет, поэтому если ты объявляешь локальный экземпляр mybar:bar в процедуре, то компилятор инициализирует ему только поля определенных типов (string,widestring,variant и интерфейсы) - все остальное будет заполнено мусором. Тоже самое при выходе из процедуры - автоматом будут очищены только эти поля - остальное останется как есть. Это же относится и к динамическому экземпляру lpmybar:^bar, т.к. New(lpmybar) зануляет, а Dispose(lpmybar) освобождает только поля указанных типов и никакие конструкторы и деструкторы автоматом не вызываются. Поэтому если нужна инициализация, то следует явно ее вызывать - можно procedure Init, а можно обозвать ее constructor Init - разница лишь в том что для конструктора допустима сокращенная запись New(lpmybar,Init(параметры)). Тоже самое и с "деструктором" Kill. Вообще он нужен только тогда, когда твой mybar содержит в себе ссылки на динамические структуры, которые ты сам создал вызовом New или GetMem и которые не освобождаются автоматически компилятором - поэтому Kill и должен их освободить. Но Kill нужно вызывать явно, либо как mybar.Kill или lpmybar.Kill+Dispose(lpmybar) или Dispose(lpmybar,Kill) если Kill объявлен как destructor.

    Вроде понятно изложил ? Или опять найдешь к чему придраться ;)



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

    > "А у вас одно слово подменяет другое. Я нигде не упоминал слово "class", а употреблял "класс" и "Object" как синонимы =)"

    Вот из-за этого и вся путанница, т.к. в дельфи object и class это разные несовместимые типы, имеющие разную организацию данных и программную реализацию.

    Экземпляр типа class в первом дворде всегда содержит указатель на VMT и это жестко закодировано во многих функциях. Сама VMT тоже имеет совершенно определенную структуру, навороченную на все случаи жизни и поэтому практически неизменную в разных версиях Delphi.

    А вот экземпляр object может вообще не иметь указателя на VMT (если ни он, ни его предки не имеют виртуальных методов) или этот указатель может располагаться по любому смещению относительно начала записи (структуры) объекта (точнее поле VMT идет после последнего поля данных того объекта, где впервые объявлен виртуальный метод). И сама VMT для object выглядит гораздо проще (по отрицательным смещениям идут размеры экземпляров по иерархии, а по положительным ссылки на виртуальные методы в порядке объявления).

    Использовать тип object конечно можно, но следует иметь в виду, что это не class и многие вещи применимые для class могут не работать для object (имхо). А для простых вещей - пожалуйста, я и сам object'ы местами использую (в качестве расширения обычных record) и все нормально работает ;)