Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Задачи _ Задачи на знание ООП

Автор: volvo 23.11.2006 14:01

Приветствую всех, зашедших в эту тему...

Такая мысль у меня зрела давно, но вот сегодня (по мотивам одного из топиков в разделе Дельфи) я все-таки решил ее материализовать... Если понравится - продолжу smile.gif

Тема создана для того, чтобы помочь начинающим программировать обратить внимание на особенности ООП, которые при написании программы и (казалось бы) "неправильной" ее работе могут вызвать недоумение.

Говорю сразу, сложных задач здесь выкладывать не буду, будут простые задачки с подвохом ( или без, это уже на мое усмотрение smile.gif ), поэтому огромная просьба: НЕ запускайте компилятор... Внимательно посмотрите на код, и попробуйте определить, что будет выведено на экран при его выполнении... Объяснения, почему Вам кажется, что будет именно так, только присветствуются. Но скрывайте посты тегом [SPОILER], чтобы своим ответом не сбивать остальных smile.gif

Значит, задача №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

Вот мое предположение:

Спойлер (Показать/Скрыть)


Пойду проверять smile.gif

Автор: мисс_граффити 23.11.2006 19:36

ИМХО:

Спойлер (Показать/Скрыть)


volvo, насчет "понравится ли" - какие могут быть сомнения? smile.gif
только в той темке, которая про Делфи, вопрос не закрытый какой-то...

Автор: klem4 23.11.2006 20:49

Спойлер (Показать/Скрыть)

Автор: volvo 23.11.2006 20:57

Андрей, внимательнее... Ты не там ошибся smile.gif

Автор: klem4 23.11.2006 21:08

Ну да это я перепутал мальца, но смысл то остается тотже

Спойлер (Показать/Скрыть)

Автор: volvo 24.11.2006 5:34

Так... Хорошо. Тогда еще одна...

Задача №2:
smile.gif Что можете сказать по поводу этого кода? Любые комментарии (без компилирования и запуска) приветствуются...

type
PT = ^T;
T = object
end;

PTint = ^Tint;
Tint = object(T)
value: integer;

procedure init(X: integer);
procedure print;
end;

PTfloat = ^Tfloat;
Tfloat = object(T)
value: double;

procedure init(X: double);
procedure print;
end;

procedure Tint.init(X: integer);
begin value := X end;
procedure Tint.print;
begin write(value:4) end;

procedure Tfloat.init(X: double);
begin value := X end;
procedure Tfloat.print;
begin write(value:8:4) end;

const
maxSize = 100;
type
TArr = object
arr: array[1 .. maxSize] of PT;

constructor init;
procedure set_index(i: integer; p: PT);

procedure count_each(var p: PT);
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(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;


var
obj_int: ^Tint;
obj_float: ^Tfloat;
any_arr: TArr;
begin
any_arr.init;
obj_int := new(PTint); obj_int^.value := 3;
any_arr.set_index(1, obj_int);
obj_float := new(PTfloat); obj_float^.value := 3.5;
any_arr.set_index(2, obj_float);
end.

Автор: Bokul 24.11.2006 6:43

Классная тема! good.gif

Цитата
Что можете сказать по поводу этого кода?

Т.е. что выведет процедура TArr.count_each(var p: PT) если ее вставить перед последним end'ом?

Автор: volvo 24.11.2006 13:14

Что выведет, например,

any_arr.count_each(new(PTfloat));

, если это вставить перед последним end-ом? smile.gif В частности, что ВООБЩЕ делает эта процедура count_each?

Автор: Bokul 25.11.2006 5:00

Цитата
В частности, что ВООБЩЕ делает эта процедура count_each?

Она считает количество элементов в массиве any_arr.arr у которых тип такой как и у передаваемого аргумента. Хотя с типом аргумента не уверен, так как вместо указателя на объект-родитель ты даешь указатель на объект-наследник.
Вопрос: считать, что при вызове проги все элементы этого массива равны nill?
Спойлер (Показать/Скрыть)

Автор: 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)
будут простые задачки с подвохом ( или без, это уже на мое усмотрение smile.gif )
Здесь как раз и было интересно, будет ли кто-то смотреть на код, и пробовать его анализировать, или код будет скопирован, и Atl+F9... Нет. Этот код не должен компилироваться. Следующим моим вопросом должен был быть вопрос "А как, собственно, заставить эту программу компилироваться", но...

В-общем, я так понимаю, что это никому не интересно кроме Bokul-а, ну что-ж... Дело ваше... Складывать числа, табулировать вывод данных и в тысячный раз делать процедуру сортировки "пузырьком" как видно, интереснее... Тему открепляю, пускай уходит вниз... Искусственно поддерживать "живучесть" я не намерен...

Автор: мисс_граффити 26.11.2006 3:41

и мне интересно.
просто в тысячный раз скинуть пузырек можно и между делом, а здесь надо посидеть подумать.
*эт я пытаюсь себя заставить зачетную работу по философии делать.... уже забила.
volvo, следующий вопрос должен был быть "как заставить компилироваться" или "как заставить выдать определенный результат, имеющий какое-то значение"?
компилироваться, думаю, будет вот так:
({$N+} не вставляла как не относящееся к названной теме)

type
PT = ^T;
T = object
{
добавляем конструктор в родительский класс - остальные пусть наследуют...
при желании можно было просто переделать init....
}
constructor create;
end;

PTint = ^Tint;
Tint = object(T)
value: integer;
procedure init(X: integer);
procedure print;
end;

PTfloat = ^Tfloat;
Tfloat = object(T)
value: double;
procedure init(X: double);
procedure print;
end;

procedure Tint.init(X: integer);
begin value := X end;
procedure Tint.print;
begin write(value:4) end;

procedure Tfloat.init(X: double);
begin value := X end;
procedure Tfloat.print;
begin write(value:8:4) end;

const
maxSize = 100;
type
TArr = object
arr: array[1 .. maxSize] of PT;

constructor init;
procedure set_index(i: integer; p: PT);

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;

constructor T.Create;
begin end;

var
obj_int: ^Tint;
obj_float: ^Tfloat;
any_arr: TArr;
begin
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));
end.

Автор: Bokul 26.11.2006 4:22

    
obj_int := new(PTint);
obj_int^.Create;


Можно заменить на
  obj_int := new(PTint,Create);

Значит я был прав насчет VMT таблицы? Выходит что компилятор сам может "вылавить" такую ошибку?

Автор: volvo 26.11.2006 4:55

Цитата
Выходит что компилятор сам может "вылавить" такую ошибку?
Не совсем... Компилятор делает медвежью услугу... С одной стороны, он контролирует в Compile-Time, что у типа, к которому применяется TypeOf, вообще НЕ МОЖЕТ быть VMT, следовательно, он не может использовать TypeOf, а с другой...

Берем код мисс_граффити... Вопрос на засыпку: будет работать корректно, или нет? smile.gif Если ДА - то что будет напечатано, если НЕТ - где будет сбой...

Автор: 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.
Значит, можно smile.gif А почему? У типа действительно нет VMT... У него вообще ничего нет, главное, чтобы VMT могла быть у экземпляра этого типа...

Автор: Bokul 26.11.2006 7:58

Спойлер (Показать/Скрыть)

Автор: volvo 26.11.2006 14:05

Bokul no1.gif Не будет того, что ты написал:

Спойлер (Показать/Скрыть)

Автор: мисс_граффити 26.11.2006 16:41

Цитата
У него вообще ничего нет, главное, чтобы VMT могла быть у экземпляра этого типа...

Значит, я слегка увлеклась и
obj_int^.Create; 
...
obj_float^.create;

на компиляцию не повлияют. что с ними, что без них...

volvo, я же уточнила, что код будет только компилироваться. Это, конечно, для корректной работы необходимо, но не достаточно.
По поводу корректной работы.
Спойлер (Показать/Скрыть)

Автор: volvo 26.11.2006 16:53

мисс_граффити blink.gif Да ты что? smile.gif Не будет и этого smile.gif Два предположения из твоих трех - ошибочны...

Если бы все было так просто, неужели же я стал бы задавать этот вопрос? blum.gif

Автор: мисс_граффити 26.11.2006 17:46

Расскажешь, как на самом деле?

Автор: volvo 26.11.2006 18:51

Цитата
Расскажешь, как на самом деле?
yes2.gif Рассказываю... (здесь описание всех ошибок, которые я намеренно внес в программу, и объяснение, почему ошибочно то или иное предположение, высказанное ранее, так что, если кто-то хочет подумать самостоятельно - не открывайте текст, и заодно подумайте об ответе на... )

Спойлер (Показать/Скрыть)

... очередной вопрос: где именно (желательно показать конкретные строки кода, и рассказать, почему именно тут) происходят в этой программе утечки памяти, и как нужно переделать программу (НЕ меняя иерархию объектов), чтобы убрать утечки, и добиться полностью правильной ее работы?

О том, что это за структура: Это что-то напоминающее http://cqc.univer.omsk.su/j2a/python/doc/ru/articles/python%20overview_files/a.htm#3.1 языка Python, только с изменяемыми элементами...

Автор: мисс_граффити 26.11.2006 19:24

Цитата
Чем же непонятное? Указатель описан, как указатель на базовый класс, значит, в любом случае, в массиве будут указатели на экземпляры одного из типов в цепочке наследования...

Спойлер (Показать/Скрыть)

Автор: volvo 26.11.2006 20:32

Цитата
Почему так?
А что, собственно, вызывает сомнения? Три объекта в иерархии, три адреса VMT... То, что есть четвертое значение?

smile.gif Так сделай
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... Попробуешь сделать это? smile.gif

Автор: 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)
Я так понял, что мы остановились на таком вызове этого метода? Сдесь не происходит выделения памяти кроме как для указателя?
smile.gif Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...

Автор: Bokul 27.11.2006 6:51

Цитата
Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...

any_arr.count_each(new(PTfloat, create ));

Ну тогда все понятно, мы передаем указатель на объект в процедуру any_arr.count_each (не зная его), он там копируется. Выходит мы получаем два указателя на один объект - один локальный (копия) и второй - который мы не знаем. Мне интересно, что делает Паскаль с этим неизвестным? От этого и зависит, наверное, решение... ИМХО.

Автор: volvo 27.11.2006 6:56

Угу... Молодец... smile.gif А что произойдет с локальной копией, когда закончится выполнение процедуры, ты мне можешь рассказать? smile.gif Даже не так... Давай начнем с такого вопроса: куда копируется переменная, переданная "по значению"? rolleyes.gif

Автор: Bokul 27.11.2006 7:02

Цитата
Угу... Молодец...

smile.gif
Цитата
Давай начнем с такого вопроса: куда копируется переменная, переданная "по значению"?

Не знаю... unsure.gif
Добавил: уже знаю, благодаря той ссылке, - в стек.

Автор: мисс_граффити 27.11.2006 23:11

Передавать как константу?

procedure count_each(const p:PT);


Автор: volvo 27.11.2006 23:26

Нет, ребята (и девушки тоже smile.gif ) - так не пойдет... Чего вы по одной строке спрашиваете? Я тоже могу сказать, что менять надо одну их этих строк, и что? Помогло это вам? Нисколько... Мне тоже вот этот момент

Цитата
Передавать как константу?
ничего не сказал... Где объяснение, что изменится в таком случае? Что будте происходить, чего происходить не будет... Полную программу в студию smile.gif Которая не дает утечек... Только проверяться она будет на моих тестах smile.gif Вот эти части:

var
(***** Начиная отсюда *****)
obj_int: ^Tint;
obj_float: ^Tfloat;
(***** Заканчивая здесь *****)

any_arr: TArr;

begin
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));

(* Здесь будет добавлен некоторый код !!! *)
end.
я изменю... Так, как позволяет синтаксис и описания объектов... А потом сравню размер свободной памяти ДО начала выполнения программы с размером ПОСЛЕ окончания работы, договорились? Естественно, программа должна реализовывать и основные функции: печать любого из элементов кортежа, проверку количества элементов того или иного типа...

smile.gif

Автор: мисс_граффити 28.11.2006 1:21

запрет на пользование компилятором в силе?
если нет - где можно размер свободной памяти проверить?

Автор: volvo 28.11.2006 1:30

Нет, теперь на пользование компилятором нет запрета... Сейчас закрыт только код, который будет добавляться мной для теста smile.gif

Но компилироваться программа должна в TP (и менять настройки среды для того, чтобы она заработала, пользователь не должен... Если нужно изменить режимы компиляции, или еще какие вещи - пользуемся директивами компилятора) smile.gif

Автор: 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

А я? 10.gif

Автор: volvo 28.11.2006 5:20

А ты - по ссылке, которую я давал раньше, там все написано smile.gif
Ничего к сказанному там я добавить не могу...

Автор: 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.


Полный код:


Прикрепленные файлы
Прикрепленный файл  Objects.PAS ( 1.75 килобайт ) Кол-во скачиваний: 201

Автор: volvo 29.11.2006 6:03

Цитата
Так?
no1.gif

Добавление 3-х строк в те участки, в которые я показал - результат в аттаче... Не все утечки убраны... blum.gif


Эскизы прикрепленных изображений
Прикрепленное изображение

Автор: Bokul 29.11.2006 6:05

Что ж там за строки blink.gif У меня максимальная утечка была 16 байт, у тебя -320 mega_chok.gif

Автор: volvo 29.11.2006 6:14

Bokul, в моем скриншоте содержится скрытая подсказка smile.gif

Автор: Bokul 1.12.2006 8:56

Все, сдаюсь... Не вижу, где убегают драгоценные байты... Идей нету, может только еще один деструктор для TArr сделать, но это уже гадание... так что прошу показать правильное решение. smile.gif

Автор: volvo 1.12.2006 13:16

Цитата
Не вижу, где убегают драгоценные байты...
Хорошо... Подсказываю. Вот тут:

procedure TArr.set_index(i: integer; p: PT);
begin if i <= maxSize then arr[i] := p; end;

Что будет, например, если я запишу 4 раза подряд в первую ячейку заново инициализированный объект? Я имею в виду, что будет с тем объектом, адрес которого там был записан РАНЬШЕ? Он останется висеть в памяти, а указатель на него ты потеряешь smile.gif

Теперь понятно, что надо поправить?

Автор: Bokul 1.12.2006 13:28

Аааа... Мелькала такая мысль, ну только я ее сразу почему-то отбросил.

Цитата
Теперь понятно, что надо поправить?

Ага yes2.gif Но я вижу три способа:
1 удалять с кучи прежний объект
2 не допускать этого
3 самому назначать индексы
Цитата
Я имею в виду, что будет с тем объектом, адрес которого там был записан РАНЬШЕ?

но смотря как передавать... если инициализировать объект прямо в методе
any_arr.set_index(1,new(PTfloat,create));

то действительно потеряем, если же через известный указатель, то не вижу никаких проблем.

Автор: volvo 1.12.2006 13:35

Цитата
если же через известный указатель, то не вижу никаких проблем.
Да? Зато я вижу: допустим, тебе надо создать кортеж из 500 элементов... Будешь создавать 500 переменных-указателей? Тогда зачем, извини, тебе кортеж... Работать-то будешь все равно через одну (максимум - две, потому что есть 2 разных типа реальных объектов, которые ты можешь создать) переменные. И все равно потеряешь старое значение при инициализации нового экземпляра...

Цитата
я вижу три способа:
Если можно - решения для всех трех - в студию smile.gif

Автор: Bokul 1.12.2006 13:42

Цитата
Если можно - решения для всех трех - в студию

Сделаем, но только через часиков 10 - сейчас спать, а завтра в школу не идти smile.gif

P.S. Я конечно понимаю, что достал уже, но я так и не понял, что Паскаль делает с этим неизвестным указателем wub.gif

Автор: 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;


Прикрепленный файл  HHHH1.PAS ( 1.93 килобайт ) Кол-во скачиваний: 359


Цитата
2 не допускать этого


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;


Прикрепленный файл  HHHH2.PAS ( 1.96 килобайт ) Кол-во скачиваний: 358


Цитата
3 самому назначать индексы


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;


Прикрепленный файл  HHHH3.PAS ( 1.81 килобайт ) Кол-во скачиваний: 369


PS Только что заметил: по времени вышло почти 10 часов, как и предсказывал, хотя я и не подстраивался smile.gif

Автор: volvo 1.12.2006 23:55

Первый вариант не убирает утечки: добавление тех же самых 3-х строк, что и раньше:
Прикрепленное изображение

Второй и третий не тестировал ...

Автор: Bokul 2.12.2006 0:50

Я не понимаю почему там не возникает ошибки.

  
obj_int := new(PTint,create);
obj_int^.value := 3;
any_arr.set_index(1, obj_int);


Сейчас 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 smile.gif

Автор: 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. Какая разница? Почему в данном варианте он видит, а там - нет?


Прикрепленные файлы
Прикрепленный файл  1.PAS ( 192 байт ) Кол-во скачиваний: 191

Автор: Bokul 2.12.2006 3:41

Извиняюсь, не увидел вопроса

Цитата
Это ты как определил?

После столько часов медитации над кодом все видно yes2.gif . Fpc не запускал.

Автор: Bokul 3.12.2006 7:21

Надо сделать деструктор объекта T виртуальным, так как для правильной очистки полиморфических объектов с памяти надо знать их размер, а виртуальный деструктор это обеспечит. Естественно у наследников надо тоже сделать деструкторы виртуальными. Правильно?????

Автор: Bokul 4.12.2006 2:13

Цитата
Надо сделать деструктор объекта T виртуальным, так как для правильной очистки полиморфических объектов с памяти надо знать их размер, а виртуальный деструктор это обеспечит. Естественно у наследников надо тоже сделать деструкторы виртуальными. Правильно?????

Или уже никто не заглядывает в эту тему?

Автор: volvo 4.12.2006 2:51

Ну, так ты программу-то приаттачь со всеми изменениями, тогда и посмотрим, остаются утечки или нет smile.gif Что же толку, если я сделаю те изменения, которые нужны?

Автор: Bokul 4.12.2006 5:03

Вот:


Прикрепленные файлы
Прикрепленный файл  HHHH2.PAS ( 2.39 килобайт ) Кол-во скачиваний: 167

Автор: volvo 4.12.2006 17:14

В принципе, почти правильно... Но нужно еще кое-что добавить, потому что программа еще пропускает, например... Ну, вот результат:


Эскизы прикрепленных изображений
Прикрепленное изображение

Автор: Bokul 5.12.2006 5:02

А утечек нету?

А с 210 я воюю уже давно, даже догадываюсь что ты поменял yes2.gif

any_arr.count_each(new(PTfloat,create));

вместо new(PTfloat,create) поставил что-то наподобие obj_float... Так?
Бороться не знаю как... Подскажешь?

Автор: Bokul 6.12.2006 5:14

Продолжаю ставить вопросы на которые никто не отвечает dry.gif
Можно ли как-то программно отличить any_arr.count_each(new(PTfloat,create) ) от any_arr.count_each(obj_float )?

Автор: volvo 6.12.2006 15:58

Отвечаю... Естественно, вопросом на вопрос: "А зачем?"

Смысл этого привести сможешь? Что тебе даст, если ты это определишь? Все равно, у тебя (почти всегда) в count_each будет передан указатель на объект. Вот и работай с ним... Какая тебе (а тем более, твоей функции count_each) разница, сконструирована инициализация объекта прямо на месте, или передан указатель, уже использованный в программе? Это что-то меняет? Значит, неправильно спроектирована программа... Исправляй так, чтобы работало независимо от способа передачи параметров... blum.gif

Автор: Bokul 7.12.2006 5:31

Цитата
Значит, неправильно спроектирована программа...

Полностью согласен. smile.gif
Цитата
Какая тебе (а тем более, твоей функции count_each) разница, сконструирована инициализация объекта прямо на месте, или передан указатель, уже использованный в программе?

А как освободить с памяти объект на который нет указателя? Вот тебе и разница... smile.gif

Вот еще вариант, мне кажется, что тоже не правильно, но идея должна быть такой, т.е. передавать в метод не указатель, а тип.



Прикрепленные файлы
Прикрепленный файл  HHHH1.PAS ( 2.21 килобайт ) Кол-во скачиваний: 196

Автор: volvo 7.12.2006 5:51

Цитата
А как освободить с памяти объект на который нет указателя?
А вот с этого места, пожалуйста, поподробнее... То есть, ты считаешь, что вот эта программа:
type
pT = ^T;
T = object
value: integer;

constructor init(x: integer);
procedure print; virtual;
end;

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 символа и она будет работать, как положено? Нет уж... no1.gif

Автор: Bokul 7.12.2006 6:04

В общем я имел ввиду объект с потерянным на него указателем, но не страшно, развития вопроса мне нравится... smile.gif

Цитата
Как же она может напечатать, если я передаю (да еще и константно) указатель на объект, который НЕ существует?

Что, он действительно не существует? blink.gif Судя по изменению памяти я бы этого не сказал...
Цитата
Объясни тогда, почему, если НЕТ указателя?

Цитата
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 ? smile.gif И где именно она может произойти (желательно показать строчку внутри процедуры, и сказать, ЧТО эту ошибку может вызвать)...

Автор: Bokul 7.12.2006 6:31

Цитата
И где именно она может произойти

Так как 210 ошибка возникает при обращению к виртуальному методу не инициализированного объекта, а единственный используемый метод это done, то следовательно такая ошибка может возникнуть только здесь -
Цитата
dispose(p,done);

Цитата
Что должно быть на месте вопросов, чтобы внутри count_each произошла Run-Time ошибка 210 ?

Указатель на не инициализированный объект, правда в таком случае ошибка произойдет раньше...

Автор: volvo 7.12.2006 6:40

Ты сам себе противоречишь, тебе не кажется? Ты покажи такой ответ, которого можно добиться... То есть, чтобы ошибка была предсказуемой... Твой предыдущий пост не отвечает на поставленный вопрос, ты вводишь сам себя в заблуждение (так ошибка произойдет на показанной тобой строке, или раньше?) Вот я сейчас могу показать, что надо сделать, чтобы произошла именно ошибка №210, а не немедленное закрытие программы, "неправильная инструкция" или еще что-то в этом роде... Следовательно, я смогу и избежать этой ошибки... Пока ты не научишься ошибки предсказывать - об их исправлении не может быть и речи blum.gif

Автор: Bokul 7.12.2006 6:57

Цитата
Указатель на не инициализированный объект, правда в таком случае ошибка произойдет раньше...

Дамм, надо отвыкать использовать универсальное местоимение ... ... smile.gif
Дело в том, что, если мы передадим в этот метод объект без инициализированной 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, а не немедленное закрытие программы, "неправильная инструкция" или еще что-то в этом роде.

Если приведенный выше вариант не правильный, покажи... smile.gif

Автор: 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

smile.gif Кстати, я нашел у тебя еще кое-что не совсем корректное... Смотри:

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

Цитата
и что я получу, догадайся?

Большую толстую ошибку smile.gif
Получаем 10 указателей на один и тот же объект, что есть не очень хорошо, так как потом мы попытаемся их все освободить из памяти при помощи виртуального деструктора, но после первого "освобождения", нам останется 9 указателей на объект без VMT, что при последующим вызове виртуального done вызовет ошибку 210.
Цитата
а проверку, которую я показал делать пользователю класса действительно не совсем корректно, хотя даже это не помогло...

Все понял. Мне вообще интересует вопрос о том, что должен знать и не знать объект.
Цитата
А дело-то все в том, что TypeOf во время компиляции не может проверить правильность таблиц виртуальных методов

Я тут уже задавал вопрос, но так и не услышал ответа: что на самом деле делает TypeOf? Возвращает значения поля виртуальных объектов у объекта?
А как она может проверить правильность этих таблиц? Ведь вся инфа об объекте хранится именно там?
Цитата
наличие правильной VMT не гарантируется ничем, кроме вызова конструктора...

Я так и не понял (вернее опять запутался) что делает конструктор. Я думал, что он заполняет ПВМ (поле виртуальных методов), а в построении таблиц он не участвует.

Вопрос Из чего состоит размер объекта? Только из его полей + , при условии существования конструктора, 4 байтов для ПВМ?

Автор: volvo 8.12.2006 15:50

Цитата
Я так и не понял (вернее опять запутался) что делает конструктор.

Цитата(http://sizov.boom.ru/books/turbo6/tpash.htm)
Как упомянуто ранее, констрактор объектного типа содержит специальный код, который сохраняет смещение VMT объектного типа в инициализируемом экземпляре.


Цитата
Я думал, что он заполняет ПВМ (поле виртуальных методов), а в построении таблиц он не участвует.
Я где-то сказал, что он участвует в построении таблиц? blink.gif Я сказал, что до тех пор, пока ты не вызовешь конструктор, с 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

Спасибо, теоретическая часть укрепилась. good.gif
То как там по поводу

Цитата
Большой толстой ошибки:
Получаем 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, только снаружи), а если "нет" - то почему? smile.gif

Автор: Bokul 9.12.2006 6:30

Цитата
procedure X(const the_pointer: pT);

А сначала ведь был Var wink.gif
Цитата
Хотя указатель передается в функцию через спецификатор Const, все-же Dispose к нему применяется, а значит, удаляется связь экземпляра с VMT

В каком смысле удаляется? wub.gif Я понимаю, что память, ранее зарезервированная под объект, уже больше не связанна с указателем на него (объект), и что в любое время она может быть переписана, но ведь ПВМ все-равно продолжает указывать на VMT объекта.
Цитата
А надо бы такое (я про использование одного указателя для нескольких ячеек) позволять... Что для этого придется сделать? Есть мысли?

Сделать только один указатель связанный с самим объектом, остальные будут связанные с ним... wacko.gif В общем над этим вопросом думаю 61 поста, нормальных идей не было...
Цитата
Можно ли добиться того, чтобы при каких-то условиях было выведено сообщение "not assigned", и если ответ = "да", то перечисли все способы, которые ты можешь себе представить для этого (ничего не меняя внутри самой процедуры X, только снаружи), а если "нет" - то почему?

if not('да') and not('не') then
writeln('не знаю ')

Автор: Bokul 9.12.2006 11:26

Цитата
Можно ли добиться того, чтобы при каких-то условиях было выведено сообщение "not assigned", и если ответ = "да", то перечисли все способы, которые ты можешь себе представить для этого (ничего не меняя внутри самой процедуры X, только снаружи), а если "нет" - то почему?

Ха, красиво! good.gif Я сначала перепутал как действует assigned, но ничего, разобрался. Не знаю, можно ли было использовать компилятор, но я использовал, в следующий раз оговаривай, пожалуйста, этот вопрос. smile.gif

И собственно ответ:
p:^T

этот указатель ссылается на место в куче, где размещены поля объекта T, а значит, если их нету - то й указатель будет nil-овым smile.gif . Выходит, что у нас есть две возможности обеспечить работу для 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 не будет - я продолжу за него, хорошо?

Если он и не против, то почему бы и нет smile.gif
Цитата
Вопрос немного меняется

Подожди. То, что я по тому вопросу написал, правильно?

Автор: Алена 10.12.2006 13:46

По тому вопросу - правильно, но там он просто ошибся, и не поставил сразу NOT в процедуру... Весь смысл был как раз в том, чтобы сделать такой вариант задания, который привела я...

Автор: Bokul 10.12.2006 23:26

Цитата
По тому вопросу - правильно, но там он просто ошибся, и не поставил сразу NOT в процедуру...

Откуда такая увереность? wink.gif
Цитата
Весь смысл был как раз в том, чтобы сделать такой вариант задания, который привела я...

Не знаю, то показалось сложнее

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 smile.gif

Автор: Bokul 11.12.2006 0:13

Цитата

Еще варианты будут?
Если нет, то я добавляю еще одно ограничение

lol.gif lol.gif lol.gif
Цитата
Your turn

d'accord smile.gif
Паскаль, вроде, сам по-началу делает все указатели равным 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.


Честно говоря не понимаю почему так. wacko.gif Если на месте ob поставить t, ничего не получится. Почему?

Автор: Алена 11.12.2006 17:14

Цитата
Честно говоря не понимаю почему так.
Потому, что этот вариант некорректен по определению. Если бы ты компилировал ЭТО в FPC, например, то получил бы Error 216 или AV. Почему?

А кто тебе сказал, что
getmem(p,sizeof(ob));
инициализирует указатель нулем? Ты что, переопределял где-то HeapFunc? По умолчанию GetMem возвращает ненулевой указатель на выделенную область памяти, а при нехватке памяти инициирует RunTime Error, то есть, корректно получить указатель равный Nil с ее помощью - проблематично.

Если TP "замалчивает" ошибку - это еще не значит, что ее нет. Насколько я знаю, эта тема как раз и создавалась для того, чтобы научить избегать подобных ошибок, а не делать их преднамеренно. Есть гораздо более корректный и красивый способ сделать то, что требовалось по условию. yes2.gif

Автор: 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

no1.gif Не пойдет... Имелся в виду другой способ... А теперь - о природе данного вопроса, почему собственно я его задал, хотя, казалось бы, предыдущий вопрос еще не решен окончательно? А вот почему. Я как раз это и хотел выяснить, знаешь ли ты, что при таком вызове

X(new(pT, Init));
в процедуру может передаваться NIL (и в каком именно случае это происходит)? Оказалось, что не знаешь... Вот отсюда и вытекает одна из проблем, с которой ты столкнулся в предыдущей задачке, решать которую тебе пришлось "костылями"...

Теперь, с подсказкой, КАК вызывать процедуру, сможешь написать код, который делает то, что требовалось? smile.gif

Автор: Bokul 14.12.2006 4:27

Цитата(volvo @ 12.12.2006 17:34) *

знаешь ли ты, что при таком вызове
X(new(pT, Init));
в процедуру может передаваться NIL (и в каком именно случае это происходит)? Оказалось, что не знаешь...

Даже хуже - я не понимаю почему... Ведь если есть конструктор, должно быть и поле виртуальных методов, а значит размер объекта в куче будет не нулевым. Тогда как new может вернуть nil?
Цитата

Теперь, с подсказкой, КАК вызывать процедуру, сможешь написать код, который делает то, что требовалось? smile.gif

Не понял, о каком коде идет речь? unsure.gif

Автор: volvo 14.12.2006 5:05

Цитата
Не понял, о каком коде идет речь?
О том коде, при добавлении которого в программу (и вызове процедуры так, как я показал), получим сообщение "not assigned"... По умолчанию ты его не получишь, нужно кое-что сделать дополнительно...

Цитата
Даже хуже - я не понимаю почему...
Вот-вот... Я догадался, в чем проблема... А раз ты не понимаешь почему это происходит, то, само собой, не проверяешь и такой возможности, что при выделении памяти под объект "на лету" тебе может вернуться нулевой указатель... Может... Дело все в том, что Constructor - это не совсем простая процедура... Это, скорее, даже не процедура, а функция... И она может возвращать True/False для статических объектов и Nil/{указатель_на_объект} для динамических...

Автор: Bokul 14.12.2006 5:56

Цитата
И она может возвращать True/False для статических объектов и Nil/{указатель_на_объект} для динамических...

Ха, действительно так good.gif Никогда не встречал информации об этом. В каких ситуациях 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? Какие преимущества дает использование конструктора для статических объектов?
А
rolleyes.gif rolleyes.gif rolleyes.gif ???????
Цитата
Вот с таким:

Я же тоже самое написал...

Автор: volvo 14.12.2006 6:04

Цитата
Какие преимущества дает использование конструктора для статических объектов?
blink.gif Попробуй обратиться к методу статического объекта, лучше - виртуальному, чтобы прочувствовать как следует, без предварительного вызова конструктора...

(главное - не запутайся, статический объект - тот, который описывается так:
var obj: TObject;
и соответственно создается в сегменте данных, а динамический - это тот, который создается через New в "куче")

Автор: Bokul 15.12.2006 5:22

Цитата
главное - не запутайся

Правильно заметил - я запутался с терминами. Теперь все ясно.

Почему не отвечаешь на самые интересные вопросы? smile.gif

Я понял, что эта задача сводится к тому, чтобы конструктор вернул 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, и все будет хорошо... smile.gif


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

Значит правильно? smile.gif

Теперь я хотел бы вернутся к вопросу о организации и освобождения с память указателей на один и тот же объект - http://forum.pascal.net.ru/index.php?s=&showtopic=13848&view=findpost&p=82653.

Как сделать правильное освобождение, чтобы потом не возникло Error 210?

Автор: volvo 16.12.2006 5:45

Цитата
Теперь я хотел бы вернутся к вопросу о организации и освобождения с память указателей на один и тот же объект
Ну что ж... Давай вернемся... smile.gif Только вначале я хотел бы тебя переадресовать вот сюда:

http://forum.pascal.net.ru/index.php?s=&showtopic=13848&view=findpost&p=81422 (сообщение №58)... Здесь ты совершил стратегическую ошибку, выбрав для себя вариант №3 - назначение индексов самому... Я, если честно, на такой вариант не рассчитывал (потому, что я объяснил, что структура представляет собой кортеж, в котором обращение идет не как в списке, чтобы сам объект назначал нумерацию, а нумерация задается мной) - поэтому вот тот вариант, который я имел в виду, когда создавал тему:

Спойлер (Показать/Скрыть)

smile.gif

Автор: Bokul 16.12.2006 6:07

Ясно, все красиво и просто.. smile.gif после того как уже посмотрел... sad.gif

Цитата
Я, если честно, на такой вариант не рассчитывал

А я на функцию copy не рассчитывал... Это специально для нее ты сделал метод make_copy виртуальным?

Ну как, продолжение будет? rolleyes.gif

М
Нет, продолжения не будет... Я, если честно, рассчитывал на бОльшую посещаемость данной темы. Но, как выяснилось, на этом форуме все считают себя достаточно разбирающимися в ООП (только вот непонятно, что ж никто не отвечает на вопросы по этой теме, редко, но все-же появляющиеся в разделе "Задачи"?), для одного человека я поддерживать тему не буду.

Закрыто...