Форум «Всё о Паскале» _ Задачи _ Задачи на знание ООП
Автор: volvo 23.11.2006 14:01
Приветствую всех, зашедших в эту тему...
Такая мысль у меня зрела давно, но вот сегодня (по мотивам одного из топиков в разделе Дельфи) я все-таки решил ее материализовать... Если понравится - продолжу
Тема создана для того, чтобы помочь начинающим программировать обратить внимание на особенности ООП, которые при написании программы и (казалось бы) "неправильной" ее работе могут вызвать недоумение.
Говорю сразу, сложных задач здесь выкладывать не буду, будут простые задачки с подвохом ( или без, это уже на мое усмотрение ), поэтому огромная просьба: НЕ запускайте компилятор... Внимательно посмотрите на код, и попробуйте определить, что будет выведено на экран при его выполнении... Объяснения, почему Вам кажется, что будет именно так, только присветствуются. Но скрывайте посты тегом [SPОILER], чтобы своим ответом не сбивать остальных
Значит, задача №1:
type TA = object constructor init;
procedure print; virtual; end;
TB = object(TA) constructor init;
procedure print; virtual; end;
constructor TA.init; begin writeln('TA.init'); end; procedure TA.print; begin writeln('TA type object'); end;
constructor TB.init; begin inherited Init; writeln('TB.init'); end; procedure TB.print; begin writeln('TB type object'); end;
procedure print(X: TA); begin X.print; end; procedure print_const(const X: TA); begin X.print; end; procedure print_var(var X: TA); begin X.print; end;
var obj: TB; begin obj.init; print(obj); print_const(obj); print_var(obj); end.
Запускаем, и на экране появляется...
Автор: hiv 23.11.2006 17:07
Можно и усложнить ;) добавив:
type PTA=^TA;
procedure print_ptr(X: PTA); begin X^.print; end;
Автор: klem4 23.11.2006 18:52
Вот мое предположение:
Спойлер(Показать/Скрыть)
TA.Init TB.Init TA type object TB type object TB type object
Пойду проверять
Автор: мисс_граффити 23.11.2006 19:36
ИМХО:
Спойлер(Показать/Скрыть)
Вызываем TB.init, а он по inherited (до того, как выполнится сам) обращается к TA.init получаем 2 строчки: TA.Init TB.Init Параметр, передаваемый по значению - по сути есть копия объекта-аргумента, то есть будет содержать только ТА-шную информацию. И методы ТА-шные. TA type object TA type object А тут передаем по адресу... Приведение типов не осуществляется, то есть работаем с ТВ. Ну и метод ТВ-шный. TB type object
volvo, насчет "понравится ли" - какие могут быть сомнения? только в той темке, которая про Делфи, вопрос не закрытый какой-то...
Автор: klem4 23.11.2006 20:49
Спойлер(Показать/Скрыть)
Вот и я рассуждал так же как и мисс_граффити, но ошибся в одном месте, по поводу:
procedure print_var(var X: TA); begin X.print; end;
Тут ведь тоже по адресу параметр передается ! Но почему-то выводится TA-шный метод ...
Автор: volvo 23.11.2006 20:57
Андрей, внимательнее... Ты не там ошибся
Автор: klem4 23.11.2006 21:08
Ну да это я перепутал мальца, но смысл то остается тотже
Спойлер(Показать/Скрыть)
в подпрограмму объект передается по адресу в print_const(obj);... И по идее в этом случае должен использваоться метод TB. Почему это не так понять опка не могу ...
Автор: volvo 24.11.2006 5:34
Так... Хорошо. Тогда еще одна...
Задача №2: Что можете сказать по поводу этого кода? Любые комментарии (без компилирования и запуска) приветствуются...
constructor TArr.init; begin end; procedure TArr.set_index(i: integer; p: PT); begin if i <= maxSize then arr[i] := p; end;
procedure TArr.count_each(var p: PT); var i, count: integer; begin count := 0; for i := 1 to maxSize do if typeof(arr[i]^) = typeof(p^) then inc(count); writeln('count = ', count); end;
Т.е. что выведет процедура TArr.count_each(var p: PT) если ее вставить перед последним end'ом?
Автор: volvo 24.11.2006 13:14
Что выведет, например,
any_arr.count_each(new(PTfloat));
, если это вставить перед последним end-ом? В частности, что ВООБЩЕ делает эта процедура count_each?
Автор: Bokul 25.11.2006 5:00
Цитата
В частности, что ВООБЩЕ делает эта процедура count_each?
Она считает количество элементов в массиве any_arr.arr у которых тип такой как и у передаваемого аргумента. Хотя с типом аргумента не уверен, так как вместо указателя на объект-родитель ты даешь указатель на объект-наследник. Вопрос: считать, что при вызове проги все элементы этого массива равны nill?
Спойлер(Показать/Скрыть)
Мне кажется должна возникнуть ошибка, так как ты инициализировал объекты не использую конструкторов, а значит теблица VMT не созданна, а значит typeof объекта без VMT вызовет ошибку:
Цитата
Can only be applied to object types that have a VMT; all other types result in an error
Автор: Bokul 26.11.2006 2:33
В общем не выдержал я решил попробовать скомпилировать (не запуская) код. Компилятору не понравилось две строчки: 1-ая
procedure Tfloat.init(X: double); begin value := X end; {Must be in 8087 mode to compile this}
Я поставил директиву {$N+}, помогло.
2-ая
procedure TArr.count_each(var p: PT); var i, count: integer; begin count := 0; for i := 1 to maxSize do if typeof(arr[i]^) = typeof(p^) then {Object type expected} inc(count); writeln('count = ', count); end;
Тут я не понимаю в чем проблема...
volvo у тебя все нормально компилировалось?
Автор: volvo 26.11.2006 2:43
А я предупреждал:
Цитата(volvo @ 23.11.2006 9:01)
будут простые задачки с подвохом ( или без, это уже на мое усмотрение )
Здесь как раз и было интересно, будет ли кто-то смотреть на код, и пробовать его анализировать, или код будет скопирован, и Atl+F9... Нет. Этот код не должен компилироваться. Следующим моим вопросом должен был быть вопрос "А как, собственно, заставить эту программу компилироваться", но...
В-общем, я так понимаю, что это никому не интересно кроме Bokul-а, ну что-ж... Дело ваше... Складывать числа, табулировать вывод данных и в тысячный раз делать процедуру сортировки "пузырьком" как видно, интереснее... Тему открепляю, пускай уходит вниз... Искусственно поддерживать "живучесть" я не намерен...
Автор: мисс_граффити 26.11.2006 3:41
и мне интересно. просто в тысячный раз скинуть пузырек можно и между делом, а здесь надо посидеть подумать. *эт я пытаюсь себя заставить зачетную работу по философии делать.... уже забила. volvo, следующий вопрос должен был быть "как заставить компилироваться" или "как заставить выдать определенный результат, имеющий какое-то значение"? компилироваться, думаю, будет вот так: ({$N+} не вставляла как не относящееся к названной теме)
type PT = ^T; T = object { добавляем конструктор в родительский класс - остальные пусть наследуют... при желании можно было просто переделать init.... } constructor create; end;
procedure count_each(p: PT); {убираем var... почему - см.ответ на предыдущий вопрос} end;
constructor TArr.init; begin end; procedure TArr.set_index(i: integer; p: PT); begin if i <= maxSize then arr[i] := p; end;
procedure TArr.count_each(p: PT); var i, count: integer; begin count := 0; for i := 1 to maxSize do if typeof(arr[i]^) = typeof(p^) then inc(count); writeln('count = ', count); end;
Значит я был прав насчет VMT таблицы? Выходит что компилятор сам может "вылавить" такую ошибку?
Автор: volvo 26.11.2006 4:55
Цитата
Выходит что компилятор сам может "вылавить" такую ошибку?
Не совсем... Компилятор делает медвежью услугу... С одной стороны, он контролирует в Compile-Time, что у типа, к которому применяется TypeOf, вообще НЕ МОЖЕТ быть VMT, следовательно, он не может использовать TypeOf, а с другой...
Берем код мисс_граффити... Вопрос на засыпку: будет работать корректно, или нет? Если ДА - то что будет напечатано, если НЕТ - где будет сбой...
Автор: Bokul 26.11.2006 5:52
Цитата
procedure count_each(p: PT); {убираем var... почему - см.ответ на предыдущий вопрос}
А это имеет значения в данном случае? Мне кажется что нет, мы же передаем не объект, а указатель на него, а значит нам все-равно будет ли он копироваться или нет. ИМХО.
Автор: volvo 26.11.2006 6:06
Цитата
передаем не объект, а указатель на него, а значит нам все-равно будет ли он копироваться или нет
Ага... Только вот не скомпилируется у тебя с Var программа, а без Var или с Const - это будет компилироваться... Объяснить, почему?
Автор: Bokul 26.11.2006 6:12
Цитата
Ага... Только вот не скомпилируется у тебя с Var программа, а без Var или с Const - это будет компилироваться...
А я уже ответ хотел писать опираясь на это...
Цитата
Объяснить, почему?
Да.
Автор: volvo 26.11.2006 6:28
Потому, что при описании
procedure count_each(Var p: PT);
компилятор ждет передачи адреса переменной, которую можно менять, то есть, не просто адреса чего-то там в памяти, а именно адрес переменной... А вот Const означает, что можно передать любой адрес, изменять-то по нему ничего нельзя, тем более - вообще БЕЗ спецификатора, можно передавать как адрес переменной, так и просто адрес, он все равно скопируется в стек, и никаких действий с объектом, на который он указывает, производиться не будет...
А теперь смотрим, КАК вызывается count_each:
any_arr.count_each(new(PTfloat));
Что передается? Адрес... Переменной-то нет... Так что с Var-ом нельзя... Внимательнее смотри не только на объявление процедуры, но и на вызов...
Автор: Bokul 26.11.2006 6:40
Супер, спасибо!!!
any_arr.count_each(new(PTfloat));
А можно брать TypeOf типа? У него же нет VMT... Или есть?
Автор: volvo 26.11.2006 6:59
Цитата
А можно брать TypeOf типа? У него же нет VMT...
Смотрим в документацию:
Цитата(TP Help)
TypeOf (function) Returns a pointer to an object type's virtual method table.
Declaration: procedure TypeOf (Param1: ObjectType) : Pointer Remarks: Param1 is either an object type identifier or an instance of an object type. Can only be applied to object types that have a VMT; all other types result in an error.
Значит, можно А почему? У типа действительно нет VMT... У него вообще ничего нет, главное, чтобы VMT могла быть у экземпляра этого типа...
Автор: Bokul 26.11.2006 7:58
Спойлер(Показать/Скрыть)
Уже какой раз пытаюсь написать ответ, постоянно вытираю - нету полного понимания. Остановился вот на чем: Будет ошибка, в any_arr.set_index(1, obj_int); мы вместо указателя на объект-предок T, даем указатель на его наследника, который занимает больший (в любом случае другой) размер в памяти. Типизированный указатель - это только адрес первого байта данных . Как мы узнаем, где находится его конец? Адрес + sizeof(тип указателя). А так как вместо ожидаемого типа, мы даем другой, то должна произойти "обрезание" передаваемого объекта, т.е. укаатель на него будет "не полным" . И потом typeof скопирует себе не весь объект, а только его часть и при попытки обратится к объекту, чтобы узнать его VMT, получим ошибку. ИМХО. Хотя...
Автор: volvo 26.11.2006 14:05
Bokul Не будет того, что ты написал:
Спойлер(Показать/Скрыть)
Дело все в том, что функция TypeOf ничего никуда не копирует... если бы она работала так, как ты написал - в нем бы вообще смысла не было: это была бы просто функция, позволяющая в Compile-Time определить тип объекта... А зачем мне тогда привязываться к VMT? Дело все в том, что TypeOf возвращает адрес VMT для объекта (или типа, в RunTime уже созданы все таблицы причем в единственном экземпляре для каждого объектного типа, что позволяет программе определить, где БУДЕТ находиться VMT экземпляра объекта, когда он будет создан)...
указатели на таблицы виртуальных методов автоматически запоминаются в реализациях объектных типов с помощью конструкторов
, так что же получается? Если объектный тип имеет таблицу виртуальных методов, то у всех его потомков тоже гарантированно будут свои таблицы, так? Таблица создается всегда, кстати... Даже если не вызвать конструктор. Конструктор - он ведь только устанавливает в экземпляре объекта ссылку на VMT, поэтому, кстати, если НЕ вызвать конструктор, а вызвать виртуальный метод - система или зависнет, или тебя просто выбросит в General Protection Fault (поскольку ты не в чистом DOS-е). Причина - программа НЕ ЗНАЕТ, где расположена VMT, но таблица-то есть...
А если таблица есть и у предка, и у потомка, то логично будет предположить, что адреса хранятся в одном и том же месте? Так зачем КОНЕЦ объекта? Компилятору важно правильно добраться до того места, где хранится адрес, а это место гарантированно существует уже в предке - следовательно, все нормально...
Так что, твои рассуждения не верны... ТАМ где ты думаешь, ошибки не будет...
Автор: мисс_граффити 26.11.2006 16:41
Цитата
У него вообще ничего нет, главное, чтобы VMT могла быть у экземпляра этого типа...
Значит, я слегка увлеклась и
obj_int^.Create; ... obj_float^.create;
на компиляцию не повлияют. что с ними, что без них...
volvo, я же уточнила, что код будет только компилироваться. Это, конечно, для корректной работы необходимо, но не достаточно. По поводу корректной работы.
Спойлер(Показать/Скрыть)
procedure TArr.set_index(i: integer; p: PT); begin if i <= maxSize then arr[i] := p; end;
при такой передаче p (при помощи указателя, да еще и без var ) приведение типов не осуществляется. в итоге у нас будет указатель на класс-наследник (тип у него будет наследниковский, то есть TFloat), однако поля и методы, введенные в наследнике, доступны не будут (ну это нам и не нужно в данной программе).... Итак, в результате мы получим странный массив, в котором один элемент - указатель на TFloat, один на TInt, а остальные - на нечто непонятное. То есть тип этого непонятного определяться будет, но он не совпадет ни с типом T, ни с типами его наследников. аналогичная ситуация и с вызовом для new(PT) - экземпляра нет, VMT не создана. Определить-то тип определим, но какой..... Получим count=0 А вот если вызвать
any_arr.count_each(obj_float);
- получим count=1.
Автор: volvo 26.11.2006 16:53
мисс_граффити Да ты что? Не будет и этого Два предположения из твоих трех - ошибочны...
Если бы все было так просто, неужели же я стал бы задавать этот вопрос?
Автор: мисс_граффити 26.11.2006 17:46
Расскажешь, как на самом деле?
Автор: volvo 26.11.2006 18:51
Цитата
Расскажешь, как на самом деле?
Рассказываю... (здесь описание всех ошибок, которые я намеренно внес в программу, и объяснение, почему ошибочно то или иное предположение, высказанное ранее, так что, если кто-то хочет подумать самостоятельно - не открывайте текст, и заодно подумайте об ответе на... )
Спойлер(Показать/Скрыть)
Значит, так... Ошибка (вернее, недочет, который выявится при некоторых установках IDE Турбо-Паскаля, ну, и естественно, в любом 32-битном компиляторе) номер один:
for i := 1 to maxSize do if typeof(arr[ i ]^) = typeof(p^) then inc(count); { <--- Здесь ... }
А именно - в попытке разыменования нулевого указателя (большинство компиляторов все-таки инициализируют все элементы arr nil-ом, потому, что эта переменная размещается не в стеке, а в куче)
исправляется очень просто:
for i := 1 to maxSize do if (arr[ i ] <> nil) and (typeof(arr[ i ]^) = typeof(p^)) then inc(count);
Второе:
Цитата(мисс_граффити: попытка объяснить происходящее)
в результате мы получим странный массив, в котором один элемент - указатель на TFloat, один на TInt, а остальные - на нечто непонятное.
Чем же непонятное? Указатель описан, как указатель на базовый класс, значит, в любом случае, в массиве будут указатели на экземпляры одного из типов в цепочке наследования... Другое дело, что некоторые указатели будут неинициализированы, но они все равно будут указателями на базовый класс.
[i]Вот, кстати, еще одна причина при создании класса явно инициализировать все его члены либо сразу нужными значениями, либо нулем (или соответствующим ему значением для типа поля): если бы все было проинициализировано нулями, программа вылетела бы независимо от ключей компиляции, а это лучше, чем выдывать похожий на правду результат, а при определенных условиях начать сбоить...
Третье: для того, чтобы процедура count_each работала корректно (выдавала правильный результат), достаточно передать в нее указатель на объект, с инициализированной VMT, т.е. или так, как показала мисс_граффити - передать один из ранее использованных указателей, для которого вызывался Create, или
any_arr.count_each(new(PTfloat, create));
Но, если кто-то подумал, что это - все... Нет... Есть еще кое-что... memory leaks, в связи с чем
... очередной вопрос: где именно (желательно показать конкретные строки кода, и рассказать, почему именно тут) происходят в этой программе утечки памяти, и как нужно переделать программу (НЕ меняя иерархию объектов), чтобы убрать утечки, и добиться полностью правильной ее работы?
О том, что это за структура: Это что-то напоминающее http://cqc.univer.omsk.su/j2a/python/doc/ru/articles/python%20overview_files/a.htm#3.1 языка Python, только с изменяемыми элементами...
Автор: мисс_граффити 26.11.2006 19:24
Цитата
Чем же непонятное? Указатель описан, как указатель на базовый класс, значит, в любом случае, в массиве будут указатели на экземпляры одного из типов в цепочке наследования...
Спойлер(Показать/Скрыть)
Добавляем явное "засовывание" в arr[3] указателя на T:
А теперь делаем Evaluate: typeof(any_arr.arr[1]^) Ptr(DSeg,$A) typeof(any_arr.arr[2]^) Ptr(DSeg,$12) typeof(any_arr.arr[3]^) Ptr(DSeg,$2) typeof(any_arr.arr[4]^) Ptr(DSeg,$1068) Почему так? И разве сравниваться будут не эти значения?
Автор: volvo 26.11.2006 20:32
Цитата
Почему так?
А что, собственно, вызывает сомнения? Три объекта в иерархии, три адреса VMT... То, что есть четвертое значение?
Так сделай
any_arr.set_index(4, nil);
и посмотри на содержимое Evaluate ...arr[4]^ и ...arr[5]^
Автор: мисс_граффити 26.11.2006 20:55
те же Ptr(DSeg,$1068) соответственно, typeof(arr[4]^)<>typeof (объект одного из классов T,TInt,TFloat) так? или я что-то совсем неправильно понимаю?
Автор: volvo 26.11.2006 21:31
Ну, естественно... Какой же тип у NIL-а? Вообще-то, обращение по nil^ приводит к тому, что в С++ называется UB (Undefined Behavior)... НЕЛЬЗЯ этого делать... Поэтому, какой бы результат ты не получила - он будет некорректным, если значение указателя эквивалентно NIL
Автор: Bokul 27.11.2006 1:21
Спасибо за ссылку.
Цитата
Дело все в том, что TypeOf возвращает адрес VMT для объекта
Т.е. она просто читает это поле:
Цитата
Это 16-битовое поле, называемое полем таблицы виртуальных методов (VMP), используется для запоминания смещения таблицы виртуальных методов в сегменте данных.
?
Цитата
и как нужно переделать программу (НЕ меняя иерархию объектов),чтобы убрать утечки, и добиться полностью правильной ее работы?
Так поможет?
var obj_int: ^Tint; obj_float: ^Tfloat; any_arr: TArr; p:pointer; begin mark(p);{запоминаем в p границу текущей не занятой памяти} any_arr.init; obj_int := new(PTint); obj_int^.Create; obj_int^.value := 3; any_arr.set_index(1, obj_int); obj_float := new(PTfloat); obj_float^.create; obj_float^.value := 3.5; any_arr.set_index(2, obj_float); any_arr.count_each(new(PTfloat)); release(p);{освобождаем всю занятую память - от p и до конца кучи} end.
Автор: volvo 27.11.2006 1:45
Нет, так не пойдет... Это не выход... Ты не убираешь утечки, а просто стираешь следы их присутствия... Я имел в виду "не допускать появления утечек"...
P.S. Заметь, я не сказал "Не изменяя объекты", я сказал "Не изменяя иерархию", т.е. не добавляя новых типов перед базовым, и между предком и потомком...
Автор: мисс_граффити 27.11.2006 2:04
Цитата(volvo @ 26.11.2006 17:31)
Ну, естественно... Какой же тип у NIL-а?
вот я его непонятным и обозвала... и написала, что он определится, но как...
Автор: Bokul 27.11.2006 6:20
Цитата
где именно происходят в этой программе утечки памяти
Утечка происходит тогда, когда мы берем больше с кучи, чем возвращаем, да? Если да, то я не вижу ни одного dispose в проге... Или я что-то путаю?
Автор: volvo 27.11.2006 6:33
Цитата
Если да, то я не вижу ни одного dispose в проге
Да... Вот я и не поставил Dispose... Попробуешь сделать это?
Автор: Bokul 27.11.2006 6:39
Цитата
Да... Вот я и не поставил Dispose...
Это хорошо, а я ищу не знаю что...
Цитата
Попробуешь сделать это?
Ага, теперь ясно в чем задача.
Цитата
any_arr.count_each(new(PTfloat, create ));
Я так понял, что мы остановились на таком вызове этого метода? Сдесь не происходит выделения памяти кроме как для указателя?
Автор: volvo 27.11.2006 6:42
Цитата(Bokul @ 27.11.2006 1:39)
Я так понял, что мы остановились на таком вызове этого метода? Сдесь не происходит выделения памяти кроме как для указателя?
Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...
Автор: Bokul 27.11.2006 6:51
Цитата
Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...
any_arr.count_each(new(PTfloat, create ));
Ну тогда все понятно, мы передаем указатель на объект в процедуру any_arr.count_each (не зная его), он там копируется. Выходит мы получаем два указателя на один объект - один локальный (копия) и второй - который мы не знаем. Мне интересно, что делает Паскаль с этим неизвестным? От этого и зависит, наверное, решение... ИМХО.
Автор: volvo 27.11.2006 6:56
Угу... Молодец... А что произойдет с локальной копией, когда закончится выполнение процедуры, ты мне можешь рассказать? Даже не так... Давай начнем с такого вопроса: куда копируется переменная, переданная "по значению"?
Автор: Bokul 27.11.2006 7:02
Цитата
Угу... Молодец...
Цитата
Давай начнем с такого вопроса: куда копируется переменная, переданная "по значению"?
Не знаю... Добавил: уже знаю, благодаря той ссылке, - в стек.
Автор: мисс_граффити 27.11.2006 23:11
Передавать как константу?
procedure count_each(const p:PT);
Автор: volvo 27.11.2006 23:26
Нет, ребята (и девушки тоже ) - так не пойдет... Чего вы по одной строке спрашиваете? Я тоже могу сказать, что менять надо одну их этих строк, и что? Помогло это вам? Нисколько... Мне тоже вот этот момент
Цитата
Передавать как константу?
ничего не сказал... Где объяснение, что изменится в таком случае? Что будте происходить, чего происходить не будет... Полную программу в студию Которая не дает утечек... Только проверяться она будет на моих тестах Вот эти части:
var (***** Начиная отсюда *****) obj_int: ^Tint; obj_float: ^Tfloat; (***** Заканчивая здесь *****)
я изменю... Так, как позволяет синтаксис и описания объектов... А потом сравню размер свободной памяти ДО начала выполнения программы с размером ПОСЛЕ окончания работы, договорились? Естественно, программа должна реализовывать и основные функции: печать любого из элементов кортежа, проверку количества элементов того или иного типа...
Автор: мисс_граффити 28.11.2006 1:21
запрет на пользование компилятором в силе? если нет - где можно размер свободной памяти проверить?
Автор: volvo 28.11.2006 1:30
Нет, теперь на пользование компилятором нет запрета... Сейчас закрыт только код, который будет добавляться мной для теста
Но компилироваться программа должна в TP (и менять настройки среды для того, чтобы она заработала, пользователь не должен... Если нужно изменить режимы компиляции, или еще какие вещи - пользуемся директивами компилятора)
Автор: Bokul 28.11.2006 4:38
Цитата
Выходит мы получаем два указателя на один объект - один локальный (копия) и второй - который мы не знаем. Мне интересно, что делает Паскаль с этим неизвестным?
Так что же компилятор делает потом с неизвестным указателем?
Автор: мисс_граффити 28.11.2006 5:03
...и как посмотреть, сколько памяти свободно?
Автор: volvo 28.11.2006 5:06
writeln(memavail);
Автор: Bokul 28.11.2006 5:18
А я?
Автор: volvo 28.11.2006 5:20
А ты - по ссылке, которую я давал раньше, там все написано Ничего к сказанному там я добавить не могу...
Автор: Bokul 29.11.2006 5:56
Так?
type PT = ^T; T = object constructor create; destructor done;{добавляем деструктор} end;
destructor T.done;{который ничего не делает} begin end;
procedure TArr.count_each(const p: PT); var i, count: integer; begin count := 0; for i := 1 to maxSize do if (arr[i]<>nil) and (typeof(arr[i]^) = typeof(p^)) then inc(count); writeln('count = ', count); dispose(p,done);{------------свобождаем память} end;
var obj_int: PTint; obj_float:PTfloat; any_arr: TArr; mem:longint; begin clrscr; mem:=memavail;{замеряем память до начала свалки} any_arr.init; obj_int := new(PTint,create); obj_int^.value := 3; any_arr.set_index(1, obj_int); obj_float := new(PTfloat,create); obj_float^.value := 3.5; any_arr.set_index(2, obj_float); any_arr.count_each(new(PTfloat,create)); dispose(obj_float,done);{чистим за собой} dispose(obj_int,done);{и это тоже} writeln('Difference : ',mem-memavail);{вроде так как и было...} readln; end.
Добавление 3-х строк в те участки, в которые я показал - результат в аттаче... Не все утечки убраны...
Эскизы прикрепленных изображений
Автор: Bokul 29.11.2006 6:05
Что ж там за строки У меня максимальная утечка была 16 байт, у тебя -320
Автор: volvo 29.11.2006 6:14
Bokul, в моем скриншоте содержится скрытая подсказка
Автор: Bokul 1.12.2006 8:56
Все, сдаюсь... Не вижу, где убегают драгоценные байты... Идей нету, может только еще один деструктор для TArr сделать, но это уже гадание... так что прошу показать правильное решение.
Автор: volvo 1.12.2006 13:16
Цитата
Не вижу, где убегают драгоценные байты...
Хорошо... Подсказываю. Вот тут:
procedure TArr.set_index(i: integer; p: PT); begin if i <= maxSize then arr[i] := p; end;
Что будет, например, если я запишу 4 раза подряд в первую ячейку заново инициализированный объект? Я имею в виду, что будет с тем объектом, адрес которого там был записан РАНЬШЕ? Он останется висеть в памяти, а указатель на него ты потеряешь
Теперь понятно, что надо поправить?
Автор: Bokul 1.12.2006 13:28
Аааа... Мелькала такая мысль, ну только я ее сразу почему-то отбросил.
Цитата
Теперь понятно, что надо поправить?
Ага Но я вижу три способа: 1 удалять с кучи прежний объект 2 не допускать этого 3 самому назначать индексы
Цитата
Я имею в виду, что будет с тем объектом, адрес которого там был записан РАНЬШЕ?
но смотря как передавать... если инициализировать объект прямо в методе
any_arr.set_index(1,new(PTfloat,create));
то действительно потеряем, если же через известный указатель, то не вижу никаких проблем.
Автор: volvo 1.12.2006 13:35
Цитата
если же через известный указатель, то не вижу никаких проблем.
Да? Зато я вижу: допустим, тебе надо создать кортеж из 500 элементов... Будешь создавать 500 переменных-указателей? Тогда зачем, извини, тебе кортеж... Работать-то будешь все равно через одну (максимум - две, потому что есть 2 разных типа реальных объектов, которые ты можешь создать) переменные. И все равно потеряешь старое значение при инициализации нового экземпляра...
Цитата
я вижу три способа:
Если можно - решения для всех трех - в студию
Автор: Bokul 1.12.2006 13:42
Цитата
Если можно - решения для всех трех - в студию
Сделаем, но только через часиков 10 - сейчас спать, а завтра в школу не идти
P.S. Я конечно понимаю, что достал уже, но я так и не понял, что Паскаль делает с этим неизвестным указателем
Автор: Bokul 1.12.2006 23:47
Цитата
1 удалять с кучи прежний объект
constructor TArr.init;{добавим некую полезную нагрузку в конструктор} var i:byte; begin for i:=1 to maxSize do {обnilим все элементы} arr[i]:=nil; end;
procedure TArr.set_index(i: integer; const p: PT); begin if i<= maxSize then begin if arr[i]=nil then arr[i]:=p else begin dispose(arr[i],done); arr[i]:=p; end; end; end;
function TArr.set_index(i: integer; const p: PT):boolean; {сделаем функцию вместо процедуры true - все нормально false - наоборот} begin if i<= maxSize then if arr[i]=nil then begin arr[i]:=p; set_index:=true; end else set_index:=false; end;
type TArr = object arr: array[1 .. maxSize] of PT; num:byte;{сколько объектов уже было добавлено} procedure set_index(const p: PT);{i нам больше не понадобится} end;
constructor TArr.init; var i:byte; begin num:=0;{!!!!!!!!!!!!!!!!!} for i:=1 to maxSize do arr[i]:=nil; end;
procedure TArr.set_index(const p: PT); begin if num< maxSize then begin inc(num); arr[num]:=p; end; end;
Сейчас obj_int и первый элемент массива any_arr.arr указывают на один и тот же объект в кучи.
any_arr.set_index(1, obj_float);
Теперь я хочу поставить первым элементом указатель на другой объект.
begin dispose(arr[i],done); arr[i]:=p; end;
Так как первый элемент не равнялся nil, то выполнение any_arr.set_index приведет к выполнению этого куска кода, в котором мы освобождаем память, выделенную под Tint, но один указатель, связанный с этим участком памяти, у нас остался - obj_int. Почему не возникает ошибки сдесь:
dispose(obj_int,done);
? Эта ж память уже не с чем не связанна...
Автор: volvo 2.12.2006 1:15
Цитата
Я не понимаю почему там не возникает ошибки.
Это ты как определил? Я бы выразился по-другому: Turbo Pascal не показывает тебе, что там есть ошибка... Попробуй тот же код (закомментировав MemAvail) прогнать в FPC
Автор: Bokul 2.12.2006 1:31
Error 210
Цитата
With range-checking on, you made a call to an object's virtual method, before the object had been initialized via a constructor call.
Почему Turbo Pascal не видит эту ошибку? Что он делает? Еще раз освобождает эту память? А если она уже была зарезервированна для другого объекта или переменной? А вот в таком случае (см. атач) получаем RunTime Error 204. Какая разница? Почему в данном варианте он видит, а там - нет?
После столько часов медитации над кодом все видно . Fpc не запускал.
Автор: Bokul 3.12.2006 7:21
Надо сделать деструктор объекта T виртуальным, так как для правильной очистки полиморфических объектов с памяти надо знать их размер, а виртуальный деструктор это обеспечит. Естественно у наследников надо тоже сделать деструкторы виртуальными. Правильно?????
Автор: Bokul 4.12.2006 2:13
Цитата
Надо сделать деструктор объекта T виртуальным, так как для правильной очистки полиморфических объектов с памяти надо знать их размер, а виртуальный деструктор это обеспечит. Естественно у наследников надо тоже сделать деструкторы виртуальными. Правильно?????
Или уже никто не заглядывает в эту тему?
Автор: volvo 4.12.2006 2:51
Ну, так ты программу-то приаттачь со всеми изменениями, тогда и посмотрим, остаются утечки или нет Что же толку, если я сделаю те изменения, которые нужны?
В принципе, почти правильно... Но нужно еще кое-что добавить, потому что программа еще пропускает, например... Ну, вот результат:
Эскизы прикрепленных изображений
Автор: Bokul 5.12.2006 5:02
А утечек нету?
А с 210 я воюю уже давно, даже догадываюсь что ты поменял
any_arr.count_each(new(PTfloat,create));
вместо new(PTfloat,create) поставил что-то наподобие obj_float... Так? Бороться не знаю как... Подскажешь?
Автор: Bokul 6.12.2006 5:14
Продолжаю ставить вопросы на которые никто не отвечает Можно ли как-то программно отличить any_arr.count_each(new(PTfloat,create) ) от any_arr.count_each(obj_float )?
Автор: volvo 6.12.2006 15:58
Отвечаю... Естественно, вопросом на вопрос: "А зачем?"
Смысл этого привести сможешь? Что тебе даст, если ты это определишь? Все равно, у тебя (почти всегда) в count_each будет передан указатель на объект. Вот и работай с ним... Какая тебе (а тем более, твоей функции count_each) разница, сконструирована инициализация объекта прямо на месте, или передан указатель, уже использованный в программе? Это что-то меняет? Значит, неправильно спроектирована программа... Исправляй так, чтобы работало независимо от способа передачи параметров...
Автор: Bokul 7.12.2006 5:31
Цитата
Значит, неправильно спроектирована программа...
Полностью согласен.
Цитата
Какая тебе (а тем более, твоей функции count_each) разница, сконструирована инициализация объекта прямо на месте, или передан указатель, уже использованный в программе?
А как освободить с памяти объект на который нет указателя? Вот тебе и разница...
Вот еще вариант, мне кажется, что тоже не правильно, но идея должна быть такой, т.е. передавать в метод не указатель, а тип.
constructor T.init(x: integer); begin value := X; end; procedure T.print; begin writeln(value); end;
procedure print_me(const p: pT); begin p^.print; end;
begin print_me(new(pT, init(20))); print_me(new(pT, init(30))); print_me(new(pT, init(40))); end.
не напечатает ничего? Как же она может напечатать, если я передаю (да еще и константно) указатель на объект, который НЕ существует? Вынужден тебя огорчить, она будет работать... Объясни тогда, почему, если НЕТ указателя?
Цитата
Полностью согласен.
Ха... А ты что думал, я дам программку, в которой надо изменить 2 символа и она будет работать, как положено? Нет уж...
Автор: Bokul 7.12.2006 6:04
В общем я имел ввиду объект с потерянным на него указателем, но не страшно, развития вопроса мне нравится...
Цитата
Как же она может напечатать, если я передаю (да еще и константно) указатель на объект, который НЕ существует?
Что, он действительно не существует? Судя по изменению памяти я бы этого не сказал...
Цитата
Объясни тогда, почему, если НЕТ указателя?
Цитата
procedure print_me(const p: pT);
А что же тогда такое p?
Автор: volvo 7.12.2006 6:16
Цитата
Что, он действительно не существует?
А теперь я тебя спрошу:
procedure TArr.count_each(const p: PT); var i, count: integer; begin count := 0; for i := 1 to num do if (typeof(arr[i]^) = typeof(p^)) then inc(count); writeln('count = ', count); dispose(p,done); end;
... any_arr.count_each( ??? );
Что должно быть на месте вопросов, чтобы внутри count_each произошла Run-Time ошибка 210 ? И где именно она может произойти (желательно показать строчку внутри процедуры, и сказать, ЧТО эту ошибку может вызвать)...
Автор: Bokul 7.12.2006 6:31
Цитата
И где именно она может произойти
Так как 210 ошибка возникает при обращению к виртуальному методу не инициализированного объекта, а единственный используемый метод это done, то следовательно такая ошибка может возникнуть только здесь -
Цитата
dispose(p,done);
Цитата
Что должно быть на месте вопросов, чтобы внутри count_each произошла Run-Time ошибка 210 ?
Указатель на не инициализированный объект, правда в таком случае ошибка произойдет раньше...
Автор: volvo 7.12.2006 6:40
Ты сам себе противоречишь, тебе не кажется? Ты покажи такой ответ, которого можно добиться... То есть, чтобы ошибка была предсказуемой... Твой предыдущий пост не отвечает на поставленный вопрос, ты вводишь сам себя в заблуждение (так ошибка произойдет на показанной тобой строке, или раньше?) Вот я сейчас могу показать, что надо сделать, чтобы произошла именно ошибка №210, а не немедленное закрытие программы, "неправильная инструкция" или еще что-то в этом роде... Следовательно, я смогу и избежать этой ошибки... Пока ты не научишься ошибки предсказывать - об их исправлении не может быть и речи
Автор: Bokul 7.12.2006 6:57
Цитата
Указатель на не инициализированный объект, правда в таком случае ошибка произойдет раньше...
Дамм, надо отвыкать использовать универсальное местоимение ... ... Дело в том, что, если мы передадим в этот метод объект без инициализированной VMT, то ошибка этапа компиляции возникнет тут :
typeof(p^)
Цитата
Ты покажи такой ответ, которого можно добиться...
var obj_int: PTint; p:pointer; begin obj_int := new(PTint,create); p:=obj_int; any_arr.count_each(p); end.
Цитата
Вот я сейчас могу показать, что надо сделать, чтобы произошла именно ошибка №210, а не немедленное закрытие программы, "неправильная инструкция" или еще что-то в этом роде.
Если приведенный выше вариант не правильный, покажи...
Автор: volvo 7.12.2006 7:11
Цитата
ошибка этапа компиляции возникнет тут :
Правда? Даже nil не даст тебе там ошибку... Тем более на этапе компиляции... Достаточно, что тип формального параметра МОЖЕТ содержать указатель на VMT - это гарантирует успешную компиляцию...
Вот мой вариант генерации ошибки 210:
any_arr.count_each(nil);
?
Автор: Bokul 7.12.2006 7:23
Цитата
Достаточно, что тип формального параметра МОЖЕТ содержать указатель на VMT - это гарантирует успешную компиляцию...
А существование VMT, гарантирует существование у объекта конструктора, деструктора или хотя бы одного виртуального метода?
Цитата
Вот мой вариант генерации ошибки 210:
TP вылетает, а FreePascal показывает 216-ую... Или это уже как следствие? При отладки в TP, как можна понять, что это 210-ая,? Она же "фатальная"?
А мой вариант не правильный?
Автор: volvo 7.12.2006 13:56
Кстати, я нашел у тебя еще кое-что не совсем корректное... Смотри:
function TArr.set_index(const p: PT):boolean; begin inc(num); if num<= maxSize then begin arr[num]:=p; set_index:=true; end else begin dec(num); dispose(p,done); set_index:=false; end; end;
- полностью твой код, у меня этого просто не было... А теперь представь, что мне надо первые 10 элементов установить в значение 3.5 - неужели для этого я должен инициализировать переменную 10 раз... Я сделал так:
obj_float := new(PTfloat,create); obj_float^.value := 3.5; for i:=1 to 10 do begin if obj_float <> nil then { <--- Вот эту проверку, кстати, пользователь, делать не должен ... } any_arr.set_index(obj_float); end;
и что я получу, догадайся?
(а проверку, которую я показал делать пользователю класса действительно не совсем корректно, хотя даже это не помогло...)
Цитата
При отладки в TP, как можна понять, что это 210-ая,? Она же "фатальная"?
У меня в верхней строке экрана сообщается, какая ошибка произошла, ничего никуда не вылетает...
Цитата
А существование VMT, гарантирует существование у объекта конструктора, деструктора или хотя бы одного виртуального метода?
Существование VMT гарантируется вызовом конструктора. А наличие деструктора и виртуальных методов (при существовании VMT) - это уже проблема программиста... Они могут быть, а могут не быть - это синтаксисом не оговорено...
С другой стороны, даже наличие у объекта 10 виртуальных методов тебе ничего не даст, если ты НЕ сконструировал экземпляр как положено, т.е. можно сказать так: наличие правильной VMT не гарантируется ничем, кроме вызова конструктора... А дело-то все в том, что TypeOf во время компиляции не может проверить правильность таблиц виртуальных методов, ибо эти самые таблицы только в RunTime создаются...
Автор: Bokul 8.12.2006 8:42
Цитата
и что я получу, догадайся?
Большую толстую ошибку Получаем 10 указателей на один и тот же объект, что есть не очень хорошо, так как потом мы попытаемся их все освободить из памяти при помощи виртуального деструктора, но после первого "освобождения", нам останется 9 указателей на объект без VMT, что при последующим вызове виртуального done вызовет ошибку 210.
Цитата
а проверку, которую я показал делать пользователю класса действительно не совсем корректно, хотя даже это не помогло...
Все понял. Мне вообще интересует вопрос о том, что должен знать и не знать объект.
Цитата
А дело-то все в том, что TypeOf во время компиляции не может проверить правильность таблиц виртуальных методов
Я тут уже задавал вопрос, но так и не услышал ответа: что на самом деле делает TypeOf? Возвращает значения поля виртуальных объектов у объекта? А как она может проверить правильность этих таблиц? Ведь вся инфа об объекте хранится именно там?
Цитата
наличие правильной VMT не гарантируется ничем, кроме вызова конструктора...
Я так и не понял (вернее опять запутался) что делает конструктор. Я думал, что он заполняет ПВМ (поле виртуальных методов), а в построении таблиц он не участвует.
Вопрос Из чего состоит размер объекта? Только из его полей + , при условии существования конструктора, 4 байтов для ПВМ?
Автор: volvo 8.12.2006 15:50
Цитата
Я так и не понял (вернее опять запутался) что делает конструктор.
Как упомянуто ранее, констрактор объектного типа содержит специальный код, который сохраняет смещение VMT объектного типа в инициализируемом экземпляре.
Цитата
Я думал, что он заполняет ПВМ (поле виртуальных методов), а в построении таблиц он не участвует.
Я где-то сказал, что он участвует в построении таблиц? Я сказал, что до тех пор, пока ты не вызовешь конструктор, с VMT правильно работать не получится, следовательно ни на SizeOf ни на TypeOf полагаться нельзя, ибо они как раз работают с тем адресом, который этим самым конструктором заносится в поле экземпляра объекта...
Цитата
Из чего состоит размер объекта? Только из его полей + , при условии существования конструктора, 4 байтов для ПВМ?
Во-первых, я уже давал тебе ссылку, а во-вторых - не 4-х а 2-х байтов (в поле хранится только 16-битное смещение, ибо компилятору, а следовательно и программе, прекрасно известно, что VMT хранится в сегменте данных, так зачем за собой таскать еще 2 лишних байта? Помнишь мою программу с елкой? На сегодняшний день в ней - работа над программой продолжается - до 3000 объектов с виртуальными функциями одновременно висят в памяти, если каждый из них будет захватывать 2 "лишних" байта - то 6К у тебя вычеркнуто из размера кучи, а она не такая уж и большая, чтобы так вольно с ней обращаться...)
Цитата
Мне вообще интересует вопрос о том, что должен знать и не знать объект.
Объект должен знать ровно столько, сколько ему необходимо для того, чтобы корректно работать... В частности, информация, как был создан указатель на объект, передаваемый ему в качестве параметра (операция @, примененная к статическому объекту, взятие Addr от типизированной константы объектного типа, или конструирование прямо "on the fly" через New) - совершенно не должна его интересовать... С любым из этих случаев работа должна производиться одинаково, для этого у тебя и объявлен тип формального параметра...
Цитата
что на самом деле делает TypeOf? Возвращает значения поля виртуальных объектов у объекта?
Возвращает нормализованный указатель на VMT для заданного типа/экземпляра (т.о., если эти указатели равны, то экземпляры принадлежат к одному и тому же типу)...
Цитата
Ведь вся инфа об объекте хранится именно там?
Там хранятся только адреса виртуальных процедур/функций, это еще не ВСЯ информация, а только ее часть.
Автор: Bokul 9.12.2006 5:11
Спасибо, теоретическая часть укрепилась. То как там по поводу
Цитата
Большой толстой ошибки: Получаем 10 указателей на один и тот же объект, что есть не очень хорошо, так как потом мы попытаемся их все освободить из памяти при помощи виртуального деструктора, но после первого "освобождения", нам останется 9 указателей на объект без VMT, что при последующим вызове виртуального done вызовет ошибку 210.
Правильно?
Автор: volvo 9.12.2006 5:43
Цитата
Правильно?
Не совсем... В том фрагменте, который я тебе привел (пост №84), происходит следующее: я получил указатель на объект. Здесь все по правилам... Дальше я хочу просто один и тот же указатель 10 раз передать в функцию, чтобы 10 последовательных ячеек были заполнены одинаковым значением.. И что? Твоя реализация мне этого не позволяет, потому что после первого же Dispose указатель становится, скажем так, невалидным (вообще-то это определение из С++, но очень оно мне нравится, я использую его и здесь)... Хотя указатель передается в функцию через спецификатор Const, все-же Dispose к нему применяется, а значит, удаляется связь экземпляра с VMT, что перечеркивает возможность дальнейшего использования как этого объекта, так и указателя на него...
А надо бы такое (я про использование одного указателя для нескольких ячеек) позволять... Что для этого придется сделать? Есть мысли?
Кстати, еще один вопрос, по ходу действия, у меня к тебе (ибо отвечаешь и интересуешься этой темой только ты, остальные видимо, знают все в совершенстве) : допустим, есть фрагмент кода:
type pT = ^T; T = object ... end;
procedure X(const the_pointer: pT); begin if assigned(the_pointer) then writeln('not assigned'); end;
var p: ^T; ... X( {что-то} ) ...
Можно ли добиться того, чтобы при каких-то условиях было выведено сообщение "not assigned", и если ответ = "да", то перечисли все способы, которые ты можешь себе представить для этого (ничего не меняя внутри самой процедуры X, только снаружи), а если "нет" - то почему?
Автор: Bokul 9.12.2006 6:30
Цитата
procedure X(const the_pointer: pT);
А сначала ведь был Var
Цитата
Хотя указатель передается в функцию через спецификатор Const, все-же Dispose к нему применяется, а значит, удаляется связь экземпляра с VMT
В каком смысле удаляется? Я понимаю, что память, ранее зарезервированная под объект, уже больше не связанна с указателем на него (объект), и что в любое время она может быть переписана, но ведь ПВМ все-равно продолжает указывать на VMT объекта.
Цитата
А надо бы такое (я про использование одного указателя для нескольких ячеек) позволять... Что для этого придется сделать? Есть мысли?
Сделать только один указатель связанный с самим объектом, остальные будут связанные с ним... В общем над этим вопросом думаю 61 поста, нормальных идей не было...
Цитата
Можно ли добиться того, чтобы при каких-то условиях было выведено сообщение "not assigned", и если ответ = "да", то перечисли все способы, которые ты можешь себе представить для этого (ничего не меняя внутри самой процедуры X, только снаружи), а если "нет" - то почему?
if not('да') and not('не') then writeln('не знаю ')
Автор: Bokul 9.12.2006 11:26
Цитата
Можно ли добиться того, чтобы при каких-то условиях было выведено сообщение "not assigned", и если ответ = "да", то перечисли все способы, которые ты можешь себе представить для этого (ничего не меняя внутри самой процедуры X, только снаружи), а если "нет" - то почему?
Ха, красиво! Я сначала перепутал как действует assigned, но ничего, разобрался. Не знаю, можно ли было использовать компилятор, но я использовал, в следующий раз оговаривай, пожалуйста, этот вопрос.
И собственно ответ:
p:^T
этот указатель ссылается на место в куче, где размещены поля объекта T, а значит, если их нету - то й указатель будет nil-овым . Выходит, что у нас есть две возможности обеспечить работу для p: 1 Добавить в объект T любое поле. 2 Создать ПВТ. Вот их реализация:
Цитата
1 Добавить в объект T любое поле.
type T = object b:byte; end;
2 Создать ПВТ.
Выбираем один из 3 способов: через конструктор:
type T = object constructor init; end;
constructor t.init; begin end;
через деструктор:
type T = object destructor done; end;
destructor t.done;; begin end;
и виртуальный метод:
type T = object procedure VirtualMethod; virtual; end;
procedure T.VirtualMethod; begin end;
Автор: volvo 9.12.2006 16:45
Я не спрашивал, как обеспечить то, что P будет работать... Я спросил - может ли процедура напечатать сообщение...
Автор: Bokul 9.12.2006 22:52
Цитата
Я не спрашивал, как обеспечить то, что P будет работать... Я спросил - может ли процедура напечатать сообщение...
Может, забыл дописать еще пару строчек. Выбираем один из методов создания поля (описанных выше), а потом -
var p: ^T; begin new(p); X(p); end.
Автор: Алена 10.12.2006 5:06
Пока volvo не будет - я продолжу за него, хорошо?
Вопрос немного меняется: пост №88 - процедуру изменяем вот так:
procedure X(const the_pointer: pT); begin if NOT assigned(the_pointer) then writeln('not assigned'); end;
Задача - не используя в программе переменных типа pT (за исключением формального параметра процедуры) заставить эту самую процедуру напечатать "not assigned"... Если есть несколько способов сделать это - приведите их ВСЕ (ответы принимаются в виде работоспособного кода, а не просто отдельными вставками "а если сделать то-то и то-то")
Автор: Bokul 10.12.2006 7:05
Цитата
Пока volvo не будет - я продолжу за него, хорошо?
Если он и не против, то почему бы и нет
Цитата
Вопрос немного меняется
Подожди. То, что я по тому вопросу написал, правильно?
Автор: Алена 10.12.2006 13:46
По тому вопросу - правильно, но там он просто ошибся, и не поставил сразу NOT в процедуру... Весь смысл был как раз в том, чтобы сделать такой вариант задания, который привела я...
Автор: Bokul 10.12.2006 23:26
Цитата
По тому вопросу - правильно, но там он просто ошибся, и не поставил сразу NOT в процедуру...
Откуда такая увереность?
Цитата
Весь смысл был как раз в том, чтобы сделать такой вариант задания, который привела я...
Не знаю, то показалось сложнее
type pT = ^T; T = object end;
procedure X(const the_pointer: pT); begin if not(assigned(the_pointer)) then writeln('not assigned'); end;
var p: ^T; begin X(nil); end.
Автор: Алена 10.12.2006 23:36
Еще варианты будут?
Если нет, то я добавляю еще одно ограничение - на использование nil ... Это слово встречаться в программе не должно (равно, как и приведение к нему, вроде X(Pointer(0)))
Your turn
Автор: Bokul 11.12.2006 0:13
Цитата
Еще варианты будут? Если нет, то я добавляю еще одно ограничение
Цитата
Your turn
d'accord Паскаль, вроде, сам по-началу делает все указатели равным nil...
type pT = ^T; T = object end;
procedure X(const the_pointer: pT); begin if the_pointer=nil then writeln('not assigned'); end;
var p: ^T; begin X(p); readln; end.
Автор: Алена 11.12.2006 1:12
А внимательно прочитать предыдущие посты - никак?
Цитата
Задача - не используя в программе переменных типа pT (за исключением формального параметра процедуры)
Я надеюсь, не надо говорить, что ^T и pT это одно и то же? Иначе никакие задачи давать просто невозможно, ибо надо давать также 20 страниц описаний, что считается нарушением, а что нет... Этого я делать не буду...
Не принято ... Кроме того, изначальное присвоение nil - это компиляторозависимо, и полагаться на это просто опасно...
Автор: Bokul 11.12.2006 6:47
Вот:
type pT=^t; T=object constructor init; end; constructor T.init; begin end;
procedure X(const the_pointer:pT); begin if the_pointer=nil then writeln('not assigned'); end;
var p:pointer; ob:t; begin p:=@p;{свяжем p с любым адресом} getmem(p,sizeof(ob));{p:=nil} x(p); readln; end.
Честно говоря не понимаю почему так. Если на месте ob поставить t, ничего не получится. Почему?
Автор: Алена 11.12.2006 17:14
Цитата
Честно говоря не понимаю почему так.
Потому, что этот вариант некорректен по определению. Если бы ты компилировал ЭТО в FPC, например, то получил бы Error 216 или AV. Почему?
А кто тебе сказал, что
getmem(p,sizeof(ob));
инициализирует указатель нулем? Ты что, переопределял где-то HeapFunc? По умолчанию GetMem возвращает ненулевой указатель на выделенную область памяти, а при нехватке памяти инициирует RunTime Error, то есть, корректно получить указатель равный Nil с ее помощью - проблематично.
Если TP "замалчивает" ошибку - это еще не значит, что ее нет. Насколько я знаю, эта тема как раз и создавалась для того, чтобы научить избегать подобных ошибок, а не делать их преднамеренно. Есть гораздо более корректный и красивый способ сделать то, что требовалось по условию.
Автор: Bokul 13.12.2006 5:11
type pT = ^T; T = object end;
pTT=^TT; TT=object(T) end;
procedure X(const the_pointer: pT); begin if the_pointer=nil then writeln('not assigned'); end;
var p: ^TT; mem:longint; begin mem:=memavail; new(p); X(p); writeln('Difference: ',mem-memavail); readln; end.
Автор: volvo 13.12.2006 5:34
Не пойдет... Имелся в виду другой способ... А теперь - о природе данного вопроса, почему собственно я его задал, хотя, казалось бы, предыдущий вопрос еще не решен окончательно? А вот почему. Я как раз это и хотел выяснить, знаешь ли ты, что при таком вызове
X(new(pT, Init));
в процедуру может передаваться NIL (и в каком именно случае это происходит)? Оказалось, что не знаешь... Вот отсюда и вытекает одна из проблем, с которой ты столкнулся в предыдущей задачке, решать которую тебе пришлось "костылями"...
Теперь, с подсказкой, КАК вызывать процедуру, сможешь написать код, который делает то, что требовалось?
Автор: Bokul 14.12.2006 4:27
Цитата(volvo @ 12.12.2006 17:34)
знаешь ли ты, что при таком вызове
X(new(pT, Init));
в процедуру может передаваться NIL (и в каком именно случае это происходит)? Оказалось, что не знаешь...
Даже хуже - я не понимаю почему... Ведь если есть конструктор, должно быть и поле виртуальных методов, а значит размер объекта в куче будет не нулевым. Тогда как new может вернуть nil?
Цитата
Теперь, с подсказкой, КАК вызывать процедуру, сможешь написать код, который делает то, что требовалось?
Не понял, о каком коде идет речь?
Автор: volvo 14.12.2006 5:05
Цитата
Не понял, о каком коде идет речь?
О том коде, при добавлении которого в программу (и вызове процедуры так, как я показал), получим сообщение "not assigned"... По умолчанию ты его не получишь, нужно кое-что сделать дополнительно...
Цитата
Даже хуже - я не понимаю почему...
Вот-вот... Я догадался, в чем проблема... А раз ты не понимаешь почему это происходит, то, само собой, не проверяешь и такой возможности, что при выделении памяти под объект "на лету" тебе может вернуться нулевой указатель... Может... Дело все в том, что Constructor - это не совсем простая процедура... Это, скорее, даже не процедура, а функция... И она может возвращать True/False для статических объектов и Nil/{указатель_на_объект} для динамических...
Автор: Bokul 14.12.2006 5:56
Цитата
И она может возвращать True/False для статических объектов и Nil/{указатель_на_объект} для динамических...
Ха, действительно так Никогда не встречал информации об этом. В каких ситуациях Constructor будет возвращать False, True? Какие преимущества дает использование конструктора для статических объектов?
Цитата
О том коде, при добавлении которого в программу (и вызове процедуры так, как я показал), получим сообщение "not assigned"..
Условие менялось и, чтобы не возникло путаницы переспрошу - с такой процедурой?
procedure X(const the_pointer: pT); begin if the_pointer=nil then writeln('not assigned'); end;
Автор: volvo 14.12.2006 6:00
Вот с таким:
procedure X(const the_pointer: pT); begin if not assigned(the_pointer) then writeln('not assigned'); end;
Аленка была абсолютна права, я сразу просто забыл поставить not, она же мне и напомнила...
Автор: Bokul 14.12.2006 6:02
Цитата
. В каких ситуациях Constructor будет возвращать False, True? Какие преимущества дает использование конструктора для статических объектов?
А ???????
Цитата
Вот с таким:
Я же тоже самое написал...
Автор: volvo 14.12.2006 6:04
Цитата
Какие преимущества дает использование конструктора для статических объектов?
Попробуй обратиться к методу статического объекта, лучше - виртуальному, чтобы прочувствовать как следует, без предварительного вызова конструктора...
(главное - не запутайся, статический объект - тот, который описывается так:
var obj: TObject;
и соответственно создается в сегменте данных, а динамический - это тот, который создается через New в "куче")
Автор: Bokul 15.12.2006 5:22
Цитата
главное - не запутайся
Правильно заметил - я запутался с терминами. Теперь все ясно.
Почему не отвечаешь на самые интересные вопросы?
Я понял, что эта задача сводится к тому, чтобы конструктор вернул false?
Цитата
По умолчанию, когда для динамического экземпляра объекта не хватает памяти, вызов конструктора, использующий расширенный син- таксис стандартной процедуры New, генерирует ошибку этапа выпол- нения 203. Если вы установили функцию обработки ошибки динамичес- ки распределяемой области, которая вместо стандартного результата функции 0 возвращает 1, когда выполнить запрос невозможно, вызов конструктора через New возвращает nil (вместо прерывания програм- мы).
http://zeus.sai.msu.ru:7000/programming/bp70_lr/lr9.shtml#11 Так что, надо замусорить всю память в куче для решение?
Автор: Bokul 15.12.2006 6:24
Разобрался, даже память не надо использовать, просто в конструкторе вызвать Fail, и все будет хорошо...
type pT=^T; T=object constructor init; destructor done; end; constructor T.init; begin done; fail; end;
destructor T.done; begin end;
procedure x(const the_pointer:pT); begin if the_pointer=nil then writeln('not assigned'); end;
var mem:longint; begin mem:=memavail; X(new(pT,init)); writeln('Difference: ',mem-memavail); readln; end.
Автор: Bokul 16.12.2006 0:32
Значит правильно?
Теперь я хотел бы вернутся к вопросу о организации и освобождения с память указателей на один и тот же объект - http://forum.pascal.net.ru/index.php?s=&showtopic=13848&view=findpost&p=82653.
Как сделать правильное освобождение, чтобы потом не возникло Error 210?
Автор: volvo 16.12.2006 5:45
Цитата
Теперь я хотел бы вернутся к вопросу о организации и освобождения с память указателей на один и тот же объект
Ну что ж... Давай вернемся... Только вначале я хотел бы тебя переадресовать вот сюда:
http://forum.pascal.net.ru/index.php?s=&showtopic=13848&view=findpost&p=81422 (сообщение №58)... Здесь ты совершил стратегическую ошибку, выбрав для себя вариант №3 - назначение индексов самому... Я, если честно, на такой вариант не рассчитывал (потому, что я объяснил, что структура представляет собой кортеж, в котором обращение идет не как в списке, чтобы сам объект назначал нумерацию, а нумерация задается мной) - поэтому вот тот вариант, который я имел в виду, когда создавал тему:
Спойлер(Показать/Скрыть)
Код
type PT = ^T; T = object constructor init; destructor done; virtual;
constructor TArr.init; var i: integer; begin for i := 1 to maxSize do arr[i] := nil; end; destructor TArr.done; var i: integer; begin for i := 1 to maxSize do if assigned(arr[i]) then dispose(arr[i], done); end;
procedure TArr.set_index(i: integer; p: PT); var new_instance: PT; begin if i <= maxSize then begin if assigned(arr[i]) then dispose(arr[i], done);
arr[i] := p^.make_copy; end;
if assigned(p) then dispose(p, done); end;
procedure TArr.count_each(const p: PT); var i, count: integer; begin count := 0; for i := 1 to maxSize do begin
if assigned(p) and (typeof(arr[i]^) = typeof(p^)) then inc(count);
end; writeln('count = ', count);
if assigned(p) then dispose(p, done); end;
function copy(p: PT): PT; begin copy := p^.make_copy; end;
var i: integer; obj_int: PTint; obj_float: PTfloat; any_arr: TArr; var the_mem: longint;
begin the_mem := memavail;
any_arr.init;
{ Вызываешь хоть так - через заранее инициализированный объект: } obj_int := new(PTint, init(3)); any_arr.set_index(1, copy(obj_int)); dispose(obj_int, done); { <--- не забываем - правил хорошего тона никто не отменял !!! } { если инициализировали объект отдельно - то и освобождать его будем отдельно }
{ Хочешь - вот так, через инициализацию "on the fly" } any_arr.set_index(2, new(PTint, init(3)));
{ Ну, и то, о чем я говорил - инициализация одним объектом нескольких ячеек - тоже работает } obj_float := new(PTfloat, init(3.5)); for i := 7 to 76 do any_arr.set_index(i, copy(obj_float)); dispose(obj_float, done); { <--- здесь тоже освобождаем, раз инициализация была отдельной }
any_arr.count_each(new(ptfloat, init(0)));
any_arr.done;
writeln('difference: ', the_mem - memavail);
end.
Автор: Bokul 16.12.2006 6:07
Ясно, все красиво и просто.. после того как уже посмотрел...
Цитата
Я, если честно, на такой вариант не рассчитывал
А я на функцию copy не рассчитывал... Это специально для нее ты сделал метод make_copy виртуальным?
Ну как, продолжение будет?
М
Нет, продолжения не будет... Я, если честно, рассчитывал на бОльшую посещаемость данной темы. Но, как выяснилось, на этом форуме все считают себя достаточно разбирающимися в ООП (только вот непонятно, что ж никто не отвечает на вопросы по этой теме, редко, но все-же появляющиеся в разделе "Задачи"?), для одного человека я поддерживать тему не буду.