2018-08-02 22:04:49 +03:00
|
|
|
|
<html>
|
|
|
|
|
|
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<object type="application/x-oleobject" classid="clsid:1e2a7bd0-dab9-11d0-b93a-00c04fc99f9e">
|
|
|
|
|
|
<param name="Keyword" value="virtual">
|
|
|
|
|
|
<param name="Keyword" value="override">
|
|
|
|
|
|
<param name="Keyword" value="reintroduce">
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<param name="Keyword" value="Полиморфизм">
|
|
|
|
|
|
<param name="Keyword" value="Виртуальный метод">
|
|
|
|
|
|
<param name="Keyword" value="Динамический тип">
|
|
|
|
|
|
<param name="Keyword" value="Раннее связывание">
|
|
|
|
|
|
<param name="Keyword" value="Позднее связывание">
|
2018-08-02 22:04:49 +03:00
|
|
|
|
</object>
|
|
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
|
|
|
|
|
<title></title>
|
|
|
|
|
|
<link rel="StyleSheet" href="../../default.css">
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
<body>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<H1>Виртуальные методы и полиморфизм</H1>
|
|
|
|
|
|
<p><font color="#FF0000"><i><b>Полиморфизм</b></i></font> (от <i>греч</i>.
|
|
|
|
|
|
"много форм") - это свойство классов, связанных наследованием, иметь различную
|
|
|
|
|
|
реализацию входящих в них методов, и способность
|
|
|
|
|
|
переменной базового класса вызывать методы того класса, объект которого
|
|
|
|
|
|
содержится в этой переменной в момент вызова метода.</p>
|
|
|
|
|
|
<p>Полиморфизм используется в ситуации, когда для
|
|
|
|
|
|
группы взаимосвязанных объектов требуется выполнить единое действие, но каждый
|
|
|
|
|
|
из этих объектов должен выполнить указанное действие по-своему (т.е. у действия
|
|
|
|
|
|
возникает много форм). Для этого определяется базовый для всех объектов класс с
|
|
|
|
|
|
виртуальными методами, предусмотренными для меняющегося поведения, после чего эти методы
|
|
|
|
|
|
переопределяется в потомках. </p>
|
|
|
|
|
|
<p>Для пояснения рассмотрим переопределение метода в подклассе:</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>type</b><br>
|
|
|
|
|
|
Base =<b> class<br>
|
|
|
|
|
|
public </b><br>
|
|
|
|
|
|
<b>procedure</b> Print;<br>
|
|
|
|
|
|
<b>begin</b><br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
Println('Base');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
Derived =<b> class</b>(Base)<br>
|
|
|
|
|
|
<b> public </b><br>
|
|
|
|
|
|
<b>procedure</b> Print;<br>
|
|
|
|
|
|
<b>begin</b><br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
Println('Derived');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
<b>end</b>;</code> </p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>Присвоим переменной базового класса <code>Base</code> объект производного
|
|
|
|
|
|
класса <code>Derived</code> и вызовем метод <code>Print</code>.</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>var</b> b: Base := new Derived;<br>
|
|
|
|
|
|
b.Print;</code></p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>Какая версия метода <code>Print</code> вызывается - класса <code>Base</code> или класса <code>Derived</code>?
|
|
|
|
|
|
В данном случае решение будет принято еще на этапе компиляции: вызовется метод
|
|
|
|
|
|
<code>Print</code> класса <code>Base</code>,
|
|
|
|
|
|
заявленного при описании переменной <code>b</code>. Говорят, что имеет место
|
|
|
|
|
|
<font color="#FF0000"><b> <i>раннее связывание</i></b></font> имени метода с его
|
|
|
|
|
|
телом. Если же решение о том,
|
|
|
|
|
|
какой метод вызывать, принимается на этапе выполнения программы в зависимости от
|
|
|
|
|
|
реального типа объекта, на который ссылается переменная <code>b</code>, то
|
|
|
|
|
|
в данном случае вызывается метод <code>Derived.Print</code> (говорят
|
|
|
|
|
|
также, что имеет место <i><font color="#FF0000"><b>позднее связывание</b></font></i>). Методы, для которых
|
|
|
|
|
|
реализуется позднее связывание, называются <i><font color="#FF0000"><b>виртуальными</b></font></i>,
|
|
|
|
|
|
а переменная базового класса, через которую осуществляется вызов виртуального
|
|
|
|
|
|
метода, - <i><font color="#FF0000"><b>полиморфной переменной</b></font></i>.
|
|
|
|
|
|
Таким образом, полиморфизм реализуется вызовом виртуальных функций через
|
|
|
|
|
|
переменную базового класса. Тип класса, который хранится в данной переменной на
|
|
|
|
|
|
этапе выполнения, называется <i><font color="#FF0000"><b>динамическим типом</b></font></i>
|
|
|
|
|
|
этой переменной. </p>
|
|
|
|
|
|
<p>Для того чтобы сделать метод виртуальным, следует в объявлении этого метода
|
|
|
|
|
|
после заголовка указать ключевое слово <code><b>virtual</b></code> с последующей <code>;</code>. Для переопределения виртуального метода
|
|
|
|
|
|
следует использовать ключевое слово <code><b>override</b></code>:</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>type</b><br>
|
|
|
|
|
|
Base =<b> class<br>
|
|
|
|
|
|
public </b><br>
|
|
|
|
|
|
<b>procedure</b> Print; <b>virtual</b>;<br>
|
|
|
|
|
|
<b>begin</b><br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
Println('Base');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
Derived =<b> class</b>(Base)<br>
|
|
|
|
|
|
<b> public </b><br>
|
|
|
|
|
|
<b>procedure</b> Print; <b>override</b>;<br>
|
|
|
|
|
|
<b>begin</b><br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
Println('Derived');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br>
|
|
|
|
|
|
<b>end</b>;</code> </p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>Теперь в аналогичном участке кода.</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>var</b> b: Base := new Derived;<br>
|
|
|
|
|
|
b.Print;</code></p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>вызывается метод <code>Print</code> класса <code>
|
|
|
|
|
|
Derived</code> за счет того что решение о вызове метода откладывается на этап
|
|
|
|
|
|
выполнения программы.</p>
|
|
|
|
|
|
<p>Говорят, что методы <code>Print</code> завязаны в
|
|
|
|
|
|
<font color="#FF0000"><i><b>цепочку виртуальности</b></i></font>. Чтобы
|
|
|
|
|
|
разорвать ее (не вызывать методы в подклассах виртуально) используется ключевое
|
|
|
|
|
|
слово <code><b>reintroduce</b></code>:</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>type</b><br> DerivedTwice1 =<b> class</b>(Derived)<br>
|
|
|
|
|
|
<b> public </b><br> <b>procedure</b> Print; </code><code><b>reintroduce</b></code><code>;<br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
<b>begin</b><br> Println('DerivedTwice1');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br> <b>end</b>;</code> </p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>Если мы хотим начать новую цепочку виртуальности, то следует использовать и
|
|
|
|
|
|
<code><b>virtual</b></code> и <code><b>reintroduce</b></code>: </p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<blockquote>
|
|
|
|
|
|
<p><code><b>type</b><br> DerivedTwice2 =<b> class</b>(Derived)<br>
|
|
|
|
|
|
<b> public </b><br> <b>procedure</b> Print; </code><code><b>virtual</b>; </code><code>
|
|
|
|
|
|
<b>reintroduce</b>;<br>
|
2025-11-11 22:04:03 +03:00
|
|
|
|
<b>begin</b><br> Println('DerivedTwice2');<br>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
<b>end</b>;<br> <b>end</b>;</code> </p>
|
|
|
|
|
|
</blockquote>
|
2025-11-07 16:12:31 +03:00
|
|
|
|
<p>Если переопределить виртуальную функцию невиртуальной без ключевого слова
|
|
|
|
|
|
<code><b>reintroduce</b></code>, то ошибки не произойдет - будет выведено лишь
|
|
|
|
|
|
предупреждение о том, что цепочка виртуальности нарушена. Таким образом,
|
|
|
|
|
|
ключевое слово <code><b>reintroduce</b></code> в этой
|
|
|
|
|
|
ситуации лишь подавляет вывод предупреждения.</p>
|
|
|
|
|
|
<p>При переопределении виртуального метода в подклассе его уровень доступа
|
|
|
|
|
|
должен быть не ниже, чем в базовом классе. Например, <code><b>public</b></code>
|
|
|
|
|
|
виртуальный метод не может быть переопределен в подклассе <code><b>private</b></code>-методом.</p>
|
2018-08-02 22:04:49 +03:00
|
|
|
|
</body>
|
|
|
|
|
|
|
|
|
|
|
|
</html>
|