Помощь - Поиск - Пользователи - Календарь
Полная версия: Задачи на знание ООП
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
Страницы: 1, 2, 3
volvo
Приветствую всех, зашедших в эту тему...

Такая мысль у меня зрела давно, но вот сегодня (по мотивам одного из топиков в разделе Дельфи) я все-таки решил ее материализовать... Если понравится - продолжу 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
Можно и усложнить ;)
добавив:
type PTA=^TA;

procedure print_ptr(X: PTA);
begin
X^.print;
end;

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

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


Пойду проверять smile.gif
мисс_граффити
ИМХО:
Спойлер (Показать/Скрыть)


volvo, насчет "понравится ли" - какие могут быть сомнения? smile.gif
только в той темке, которая про Делфи, вопрос не закрытый какой-то...
klem4
Спойлер (Показать/Скрыть)
volvo
Андрей, внимательнее... Ты не там ошибся smile.gif
klem4
Ну да это я перепутал мальца, но смысл то остается тотже
Спойлер (Показать/Скрыть)
volvo
Так... Хорошо. Тогда еще одна...

Задача №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
Классная тема! good.gif
Цитата
Что можете сказать по поводу этого кода?

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

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

Она считает количество элементов в массиве any_arr.arr у которых тип такой как и у передаваемого аргумента. Хотя с типом аргумента не уверен, так как вместо указателя на объект-родитель ты даешь указатель на объект-наследник.
Вопрос: считать, что при вызове проги все элементы этого массива равны nill?
Спойлер (Показать/Скрыть)
Bokul
В общем не выдержал я решил попробовать скомпилировать (не запуская) код.
Компилятору не понравилось две строчки:
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
А я предупреждал:
Цитата(volvo @ 23.11.2006 9:01)
будут простые задачки с подвохом ( или без, это уже на мое усмотрение smile.gif )
Здесь как раз и было интересно, будет ли кто-то смотреть на код, и пробовать его анализировать, или код будет скопирован, и Atl+F9... Нет. Этот код не должен компилироваться. Следующим моим вопросом должен был быть вопрос "А как, собственно, заставить эту программу компилироваться", но...

В-общем, я так понимаю, что это никому не интересно кроме Bokul-а, ну что-ж... Дело ваше... Складывать числа, табулировать вывод данных и в тысячный раз делать процедуру сортировки "пузырьком" как видно, интереснее... Тему открепляю, пускай уходит вниз... Искусственно поддерживать "живучесть" я не намерен...
мисс_граффити
и мне интересно.
просто в тысячный раз скинуть пузырек можно и между делом, а здесь надо посидеть подумать.
*эт я пытаюсь себя заставить зачетную работу по философии делать.... уже забила.
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
    
obj_int := new(PTint);
obj_int^.Create;


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

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

Берем код мисс_граффити... Вопрос на засыпку: будет работать корректно, или нет? smile.gif Если ДА - то что будет напечатано, если НЕТ - где будет сбой...
Bokul
Цитата
procedure count_each(p: PT); {убираем var... почему - см.ответ на предыдущий вопрос}

А это имеет значения в данном случае? Мне кажется что нет, мы же передаем не объект, а указатель на него, а значит нам все-равно будет ли он копироваться или нет. ИМХО.
volvo
Цитата
передаем не объект, а указатель на него, а значит нам все-равно будет ли он копироваться или нет
Ага... Только вот не скомпилируется у тебя с Var программа, а без Var или с Const - это будет компилироваться... Объяснить, почему?
Bokul
Цитата
Ага... Только вот не скомпилируется у тебя с Var программа, а без Var или с Const - это будет компилироваться...

А я уже ответ хотел писать опираясь на это...
Цитата
Объяснить, почему?

Да.
volvo
Потому, что при описании
procedure count_each(Var p: PT);
компилятор ждет передачи адреса переменной, которую можно менять, то есть, не просто адреса чего-то там в памяти, а именно адрес переменной... А вот Const означает, что можно передать любой адрес, изменять-то по нему ничего нельзя, тем более - вообще БЕЗ спецификатора, можно передавать как адрес переменной, так и просто адрес, он все равно скопируется в стек, и никаких действий с объектом, на который он указывает, производиться не будет...

А теперь смотрим, КАК вызывается count_each:
any_arr.count_each(new(PTfloat));
Что передается? Адрес... Переменной-то нет... Так что с Var-ом нельзя... Внимательнее смотри не только на объявление процедуры, но и на вызов...
Bokul
Супер, спасибо!!!
any_arr.count_each(new(PTfloat));

А можно брать TypeOf типа? У него же нет VMT... Или есть?
volvo
Цитата
А можно брать 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
Спойлер (Показать/Скрыть)
volvo
Bokul no1.gif Не будет того, что ты написал:

Спойлер (Показать/Скрыть)
мисс_граффити
Цитата
У него вообще ничего нет, главное, чтобы VMT могла быть у экземпляра этого типа...

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

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

volvo, я же уточнила, что код будет только компилироваться. Это, конечно, для корректной работы необходимо, но не достаточно.
По поводу корректной работы.
Спойлер (Показать/Скрыть)
volvo
мисс_граффити blink.gif Да ты что? smile.gif Не будет и этого smile.gif Два предположения из твоих трех - ошибочны...

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

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

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

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

Спойлер (Показать/Скрыть)
volvo
Цитата
Почему так?
А что, собственно, вызывает сомнения? Три объекта в иерархии, три адреса VMT... То, что есть четвертое значение?

smile.gif Так сделай
any_arr.set_index(4, nil);
и посмотри на содержимое Evaluate ...arr[4]^ и ...arr[5]^
мисс_граффити
те же Ptr(DSeg,$1068)
соответственно, typeof(arr[4]^)<>typeof (объект одного из классов T,TInt,TFloat)
так?
или я что-то совсем неправильно понимаю?
volvo
Ну, естественно... Какой же тип у NIL-а? Вообще-то, обращение по nil^ приводит к тому, что в С++ называется UB (Undefined Behavior)... НЕЛЬЗЯ этого делать... Поэтому, какой бы результат ты не получила - он будет некорректным, если значение указателя эквивалентно NIL
Bokul
Спасибо за ссылку.
Цитата
Дело все в том, что 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
Нет, так не пойдет... Это не выход... Ты не убираешь утечки, а просто стираешь следы их присутствия... Я имел в виду "не допускать появления утечек"...

P.S. Заметь, я не сказал "Не изменяя объекты", я сказал "Не изменяя иерархию", т.е. не добавляя новых типов перед базовым, и между предком и потомком...
мисс_граффити
Цитата(volvo @ 26.11.2006 17:31) *

Ну, естественно... Какой же тип у NIL-а?

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

Утечка происходит тогда, когда мы берем больше с кучи, чем возвращаем, да? Если да, то я не вижу ни одного dispose в проге... Или я что-то путаю?
volvo
Цитата
Если да, то я не вижу ни одного dispose в проге
Да... Вот я и не поставил Dispose... Попробуешь сделать это? smile.gif
Bokul
Цитата
Да... Вот я и не поставил Dispose...

Это хорошо, а я ищу не знаю что...
Цитата
Попробуешь сделать это?

Ага, теперь ясно в чем задача.
Цитата
any_arr.count_each(new(PTfloat, create ));

Я так понял, что мы остановились на таком вызове этого метода? Сдесь не происходит выделения памяти кроме как для указателя?
volvo
Цитата(Bokul @ 27.11.2006 1:39)
Я так понял, что мы остановились на таком вызове этого метода? Сдесь не происходит выделения памяти кроме как для указателя?
smile.gif Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...
Bokul
Цитата
Как так не происходит? А экземпляр объекта где хранится? Не в указателе же...

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

Ну тогда все понятно, мы передаем указатель на объект в процедуру any_arr.count_each (не зная его), он там копируется. Выходит мы получаем два указателя на один объект - один локальный (копия) и второй - который мы не знаем. Мне интересно, что делает Паскаль с этим неизвестным? От этого и зависит, наверное, решение... ИМХО.
volvo
Угу... Молодец... smile.gif А что произойдет с локальной копией, когда закончится выполнение процедуры, ты мне можешь рассказать? smile.gif Даже не так... Давай начнем с такого вопроса: куда копируется переменная, переданная "по значению"? rolleyes.gif
Bokul
Цитата
Угу... Молодец...

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

Не знаю... unsure.gif
Добавил: уже знаю, благодаря той ссылке, - в стек.
мисс_граффити
Передавать как константу?
procedure count_each(const p:PT);

volvo
Нет, ребята (и девушки тоже 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
мисс_граффити
запрет на пользование компилятором в силе?
если нет - где можно размер свободной памяти проверить?
volvo
Нет, теперь на пользование компилятором нет запрета... Сейчас закрыт только код, который будет добавляться мной для теста smile.gif

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

Так что же компилятор делает потом с неизвестным указателем?
мисс_граффити
...и как посмотреть, сколько памяти свободно?
volvo
writeln(memavail);
Bokul
А я? 10.gif
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.