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

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

Форум «Всё о Паскале» _ Теоретические вопросы _ ООП

Автор: Client 15.02.2009 20:00

Всем привет!
Начал изучать ООП и появились вопросы. Вроде с наследованием и инкапсуляцией понятно, а вот с полиморфизмом не понятно. Можете объяснить для чего он нужен и что такое виртуальные методы?

Автор: volvo 15.02.2009 20:19

Ну, ты же здесь был: http://forum.pascal.net.ru/index.php?showtopic=2085 ? Там написано ,что такое полиморфизм, и как он реализуется... Что-то непонятно?

Автор: Client 15.02.2009 20:27

Цитата
Полиморфизм.
Возможность определения единого по имени действия (процедуры или функции), применимого одновременно ко всем объектам иерархии наследования, причем каждый объект может "заказывать" особенность реализации этого действия над "самим собой".
В наследовании есть родитель и потомок, где потомок сохраняет поля и методы родителя. А в полиморфизме наоборот?
Цитата
причем каждый объект может "заказывать" особенность реализации этого действия над "самим собой".
Это совсем не понятно(

Автор: volvo 15.02.2009 21:01

Не путай теплое с мягким... Наследование - это необходимое условие полиморфизма, нельзя сделать полиморфность без наследования. Смотри:

type
ta = object
constructor init;
procedure print; virtual;
end;

constructor ta.init;
begin end;
procedure ta.print;
begin writeln('TA object') end;

procedure proc(var obj: ta);
begin
obj.print;
end;

type
tb = object(ta)
constructor init;
procedure print; virtual;
end;

constructor tb.init;
begin inherited init; end;
procedure tb.print;
begin writeln('TB object') end;

var
A: ta;
B: tb;
begin
a.init; proc(a);
b.init; proc(b);
end.
Чувствуешь? Proc принимает объект типа TA или его потомка (неважно, насколько далекого, может "сын", а может и "внук", "правнук" и т.д.), и вызывает метод нужного типа. То есть, для того, чтобы напечатать значение любого потомка достаточно "заказать" в определенном классе нужную тебе функциональность. Я "заказал" реализацию TB.print, что заказал, то и получил... Только наследованием (без виртуализации) этого добиться нельзя. Если в вышеприведенном примере не будет виртуальных методов, то процедура не будет полиморфной, она для любого переданного объекта будет вызывать метод TA.print...

Автор: Client 15.02.2009 21:16

Цитата
Наследование - это необходимое условие полиморфизма, нельзя сделать полиморфность без наследования.
O_o незнал.
procedure proc(var obj: ta);
begin
obj.print;
end;
Понял, для каждого потомка будет свой метод smile.gif
constructor tb.init;
begin inherited init; end;

А что это такое inherited?
proc(b);
это тоже самое что и
b.print
Спасибо за помощь!

Автор: volvo 15.02.2009 21:37

Цитата
А что это такое inherited?
Это вызов метода из непосредственного родителя... Можно, конечно, сделать так:
constructor tb.init;
begin ta.init; end;
, но подумай, что получится, если между TA и TB будет внесен еще один наследник. Было:
type
ta = object
end;
tb = object(ta)
end;
, стало:
type
ta = object
end;
ta2 = object(ta)
end;
tb = object(ta2)
end;
, тогда прямое указание ta.init придется поменять на ta2.init, если же использовался inherited - ничего менять не надо будет...

P.S.
Да, proc(b) и b.print будут выполнять одно и то же действие, если больше ты ничего не менял в программе.

Автор: Client 15.02.2009 22:34

uses crt;
type
ta = object
constructor init;
procedure print; virtual;
end;

constructor ta.init;
begin end;
procedure ta.print;
begin writeln('TA object') end;

procedure proc(var obj: ta);
begin
obj.print;
end;

type
ta2 = object(ta)
constructor init;
procedure print; virtual;
end;

constructor ta2.init;
begin end;
procedure ta2.print;
begin writeln('TA2 object') end;


type
tb = object(ta2)
constructor init;
procedure print; virtual;
end;

constructor tb.init;
begin {inherited init;} end;
procedure tb.print;
begin writeln('TB object') end;

var
A: ta;
B: tb;
c:ta2;
begin
clrscr;
a.init; proc(a);
b.init; proc(b);
c.init; proc©;
readkey
end.
Цитата
но подумай, что получится, если между TA и TB будет внесен еще один наследник
Добавил наследника, закоментировал
inherited init;
все равно работает О_о
если я правильно понял про наследника


Добавлено через 4 мин.
Упс... Там же по идеи должны присваиваться какие-то значения полям, но их нету. Если были бы, то не прокатило, да?

Автор: volvo 15.02.2009 22:40

Цитата
все равно работает О_о
Да, работает... Только некорректно это. При инициализации потомка надо сначала инициализировать предка. Ты этого не делаешь. Хочешь, набросаю пример программы, при котором БЕЗ вызова всей цепочки Init-ов получишь аварийный вылет? smile.gif

Автор: Client 15.02.2009 22:43

Цитата
При инициализации потомка надо сначала инициализировать предка. Ты этого не делаешь
Инициализация?? т.е. присваивание полям значений или создание объекта?
Цитата
Хочешь, набросаю пример программы, при котором БЕЗ вызова всей цепочки Init-ов получишь аварийный вылет?
yes2.gif

Автор: volvo 15.02.2009 23:37

Цитата
Инициализация?? т.е. присваивание полям значений или создание объекта?
И то и другое... Вот, например, при инициализации базового типа выделяется память под строку, а все остальные - передают в этот базовый тип результат своей инициализации: Прикрепленный файл  oop_01.pas ( 920 байт ) Кол-во скачиваний: 705


Где-нибудь цепочка Init-ов прервется - будет разыменование нулевого указателя, ошибка... Я надеюсь, понятно, что в конструкторе TA2, например, если какое-то дополнительное условие не выполняется, я могу поменять строку с 'no error' на любую другую? И в конструкторе TB тоже. И даже в TA...

Автор: Client 16.02.2009 20:22

УРЯЯЯЯ!!! я понял) тут мы просто используем метод предка, т.е. если у предка 100 полей, а у потомка 2 доп. поля то можно вызвать метод предка по обработке полей и добавить действия над другими двумя полями)

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

Автор: volvo 16.02.2009 23:04

Цитата
тут мы просто используем метод предка, т.е. если у предка 100 полей, а у потомка 2 доп. поля то можно вызвать метод предка по обработке полей и добавить действия над другими двумя полями)
Если ты про конструктор - то я тебе еще больше скажу: методы потомка ДОЛЖНЫ вызывать методы предка для инициализации/присвоения его (предка) полей, потому что никто не сможет инициализировать поле лучше, чем объект, в котором это поле добавляется.

Цитата
Что-то его нету
Чего нет? Вылета? Это не проблема ООП, это проблема Турбо-Паскаля... Любой нормальный компилятор при попытке разыменовать нулевой указатель вылетит с 216-ой ошибкой... Турбо-Паскаль же ошибку замалчивает (спрашивается, ЗАЧЕМ? Программа-то не работает правильнее от того, что не происходит вылет), но при этом печатает тебе на экране откровенный бред.

Автор: Client 17.02.2009 0:29

Вроде понял) Спасибо за ответы!

Автор: fedyafed 6.02.2011 14:25

Листинг программы от Дата 15.02.2009 17:01 содержит виртуальные методы. Как нужно настроить Borland Pascal 7.0 , чтобы он с ними корректно работал?

Автор: volvo 6.02.2011 14:49

Никак не нужно настраивать, он по умолчанию прекрасно работает с виртуальностью. Если нет - меняй компилятор, у тебя кривая сборка.

Автор: Гость 6.02.2011 15:23

А overload и override компилятор не понимает(. Это уже к Delphi?