<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://camokathomelab.servebeer.com/mediawiki/index.php?action=history&amp;feed=atom&amp;title=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4</id>
	<title>Виртуальный метод - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://camokathomelab.servebeer.com/mediawiki/index.php?action=history&amp;feed=atom&amp;title=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4"/>
	<link rel="alternate" type="text/html" href="https://camokathomelab.servebeer.com/mediawiki/index.php?title=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4&amp;action=history"/>
	<updated>2026-07-18T10:40:38Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://camokathomelab.servebeer.com/mediawiki/index.php?title=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4&amp;diff=37522&amp;oldid=prev</id>
		<title>imported&gt;Alex NB OT: унификация языковых шаблонов</title>
		<link rel="alternate" type="text/html" href="https://camokathomelab.servebeer.com/mediawiki/index.php?title=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4&amp;diff=37522&amp;oldid=prev"/>
		<updated>2025-11-15T11:10:15Z</updated>

		<summary type="html">&lt;p&gt;унификация языковых шаблонов&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Полиморфизм}}&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Виртуальный метод&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;&amp;#039;виртуальная функция&amp;#039;&amp;#039;&amp;#039;) — в [[объектно-ориентированное программирование|объектно-ориентированном программировании]] метод (функция) [[класс (программирование)|класса]], который может быть переопределён в [[наследование (программирование)|классах-наследниках]] так, что конкретная реализация метода для вызова будет определяться во время исполнения. Таким образом, программисту необязательно знать точный тип [[Объект (программирование)|объекта]] для работы с ним через виртуальные методы: достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором объявлен метод. Одним из переводов слова virtual с английского языка может быть «фактический», что больше подходит по смыслу. {{Нет АИ|16|9|2020}}&lt;br /&gt;
&lt;br /&gt;
== Использование ==&lt;br /&gt;
Виртуальные методы — один из важнейших приёмов реализации [[Полиморфизм (программирование)|полиморфизма]]. Они позволяют создавать общий код, который может работать как с объектами базового класса, так и с объектами любого его класса-наследника. При этом базовый класс определяет способ работы с объектами, и любые его наследники могут предоставлять конкретную реализацию этого способа.&lt;br /&gt;
&lt;br /&gt;
== Способ определения ==&lt;br /&gt;
Одни языки программирования (например, [[C++]], [[C Sharp|C#]], [[Delphi (язык программирования)|Delphi]]) требуют явно указывать, что данный метод является виртуальным. В других языках (например, [[Java]], [[Python]]) все методы являются виртуальными по умолчанию (но только те методы, для которых это возможно; например, в Java методы с доступом &amp;#039;&amp;#039;private&amp;#039;&amp;#039; не могут быть переопределены в связи с правилами видимости).&lt;br /&gt;
&lt;br /&gt;
Базовый класс может и не предоставлять реализации виртуального метода, а только декларировать его существование. Такие методы без реализации называются «чистыми виртуальными» (перевод {{lang-en | pure virtual}}) или абстрактными. Класс, содержащий хотя бы один такой метод, тоже будет [[абстрактный класс|абстрактным]]. Объект такого класса создать нельзя (в некоторых языках допускается, но вызов абстрактного метода приведёт к ошибке). Наследники абстрактного класса должны предоставить&amp;lt;ref&amp;gt;{{Cite web|lang=ru|url=https://prog-cpp.ru/cpp-virtual/|title=Виртуальные функции|author=|website=|date=|publisher=|access-date=2020-09-16|archive-date=2020-09-24|archive-url=https://web.archive.org/web/20200924195711/https://prog-cpp.ru/cpp-virtual/|url-status=live}}&amp;lt;/ref&amp;gt; реализацию для всех его абстрактных методов, иначе они, в свою очередь, будут абстрактными классами. Абстрактный класс, который содержит только абстрактные методы, называется [[Интерфейс (объектно-ориентированное программирование)|интерфейсом]].&lt;br /&gt;
&lt;br /&gt;
== Реализация ==&lt;br /&gt;
Техника вызова виртуальных методов называется ещё «динамическим (поздним) связыванием». Имеется в виду, что имя метода, использованное в программе, связывается с адресом входа конкретного метода динамически (во время исполнения программы), а не статически (во время компиляции), так как в момент компиляции, в общем случае, невозможно определить, какая из существующих реализаций метода будет вызвана. &lt;br /&gt;
&lt;br /&gt;
В компилируемых языках программирования динамическое связывание выполняется обычно с использованием &amp;#039;&amp;#039;[[таблица виртуальных методов|таблицы виртуальных методов]]&amp;#039;&amp;#039;, которая создаётся компилятором для каждого класса, имеющего хотя бы один виртуальный метод. В элементах таблицы находятся указатели на реализации виртуальных методов, соответствующие данному классу (если в классе-потомке добавляется новый виртуальный метод, его адрес добавляется в таблицу, если в классе-потомке создаётся новая реализация виртуального метода - соответствующее поле в таблице заполняется адресом этой реализации). Таким образом, для адреса каждого виртуального метода в дереве наследования имеется одно, фиксированное смещение в таблице виртуальных методов. Каждый объект имеет техническое поле, которое при создании объекта инициализируется [[Указатель (тип данных)|указателем]] на таблицу виртуальных методов своего класса. Для вызова виртуального метода из объекта берётся указатель на соответствующую таблицу виртуальных методов, а из неё, по известному фиксированному смещению, — указатель на реализацию метода, используемого для данного класса. При использовании [[множественное наследование|множественного наследования]] ситуация несколько усложняется за счёт того, что таблица виртуальных методов становится нелинейной.&lt;br /&gt;
&lt;br /&gt;
== Пример виртуальной функции на C++ ==&lt;br /&gt;
[[Файл:ClassDiagram for VirtualFunction.png|400px|thumb|right|Диаграмма класса Animal]]&lt;br /&gt;
Пример на [[C++]], иллюстрирующий отличие виртуальных функций от невиртуальных:&lt;br /&gt;
&lt;br /&gt;
Предположим, базовый класс &amp;lt;code&amp;gt;Animal&amp;lt;/code&amp;gt; (животное) может иметь виртуальный метод &amp;lt;code&amp;gt;eat&amp;lt;/code&amp;gt; (есть, питаться, принимать пищу). Подкласс (класс-потомок) &amp;lt;code&amp;gt;Fish&amp;lt;/code&amp;gt; (рыба) переопределит метод &amp;lt;code&amp;gt;eat()&amp;lt;/code&amp;gt; не так, как его переопределит подкласс &amp;lt;code&amp;gt;Wolf&amp;lt;/code&amp;gt; (волк), но можно вызвать &amp;lt;code&amp;gt;eat()&amp;lt;/code&amp;gt; на любом экземпляре класса, унаследованного от класса &amp;lt;code&amp;gt;Animal&amp;lt;/code&amp;gt;, и получить поведение &amp;lt;code&amp;gt;eat()&amp;lt;/code&amp;gt;, соответствующее данному подклассу.&lt;br /&gt;
&lt;br /&gt;
Это позволяет программисту обрабатывать список объектов класса &amp;lt;code&amp;gt;Animal&amp;lt;/code&amp;gt;, вызывая над каждым объектом метод &amp;lt;code&amp;gt;eat()&amp;lt;/code&amp;gt;, не задумываясь о том к какому подклассу принадлежит текущий объект (то есть как питается конкретное животное).&lt;br /&gt;
&lt;br /&gt;
Интересной деталью виртуальных функций в C++ является поведение [[Аргумент по умолчанию|аргументов по умолчанию]]. При вызове виртуальной функции с аргументом по умолчанию тело функции берется у реального объекта, а значения аргументов по типу [[Ссылка (C++)|ссылки]] или указателя.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
class Animal {&lt;br /&gt;
public:&lt;br /&gt;
    void /*невиртуальный*/ move() { &lt;br /&gt;
        std::cout &amp;lt;&amp;lt; &amp;quot;This animal moves in some way&amp;quot; &amp;lt;&amp;lt; std::endl; &lt;br /&gt;
    }&lt;br /&gt;
    virtual void eat() {&lt;br /&gt;
        std::cout &amp;lt;&amp;lt; &amp;quot;Animal eat something!&amp;quot; &amp;lt;&amp;lt; std::endl; &lt;br /&gt;
    }&lt;br /&gt;
    virtual ~Animal(){} // деструктор&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
class Wolf : public Animal {&lt;br /&gt;
public:&lt;br /&gt;
    void move() { &lt;br /&gt;
        std::cout &amp;lt;&amp;lt; &amp;quot;Wolf walks&amp;quot; &amp;lt;&amp;lt; std::endl; &lt;br /&gt;
    }&lt;br /&gt;
    void eat(void) { // метод eat переопределён и тоже является виртуальным&lt;br /&gt;
        std::cout &amp;lt;&amp;lt; &amp;quot;Wolf eats meat!&amp;quot; &amp;lt;&amp;lt; std::endl; &lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
    Animal* zoo[] = {new Wolf(), new Animal()};&lt;br /&gt;
    for(Animal* a:zoo) {&lt;br /&gt;
        a-&amp;gt;move();&lt;br /&gt;
        a-&amp;gt;eat();&lt;br /&gt;
        delete a; // Так как деструктор виртуальный, для каждого &lt;br /&gt;
                  // объекта вызовется деструктор его класса&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Вывод: &amp;lt;pre&amp;gt;This animal moves in some way&lt;br /&gt;
Wolf eats meat!&lt;br /&gt;
This animal moves in some way&lt;br /&gt;
Animal eat something!&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Пример аналога виртуальных функций в PHP ==&lt;br /&gt;
Аналогом в PHP можно считать использование позднего статического связывания.&amp;lt;ref&amp;gt;{{Cite web|url=http://php.net/manual/ru/language.oop5.late-static-bindings.php|title=PHP: Позднее статическое связывание - Manual|publisher=php.net|access-date=2016-11-05|archive-date=2016-11-08|archive-url=https://web.archive.org/web/20161108043150/http://php.net/manual/ru/language.oop5.late-static-bindings.php|url-status=live}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Foo {&lt;br /&gt;
    public static function baz() {&lt;br /&gt;
        return &amp;#039;вода&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function __construct() {&lt;br /&gt;
        echo static::baz(); // позднее статическое связывание&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class Bar extends Foo {&lt;br /&gt;
    public static function baz() {&lt;br /&gt;
        return &amp;#039;огонь&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
new Foo(); // выведет &amp;#039;вода&amp;#039;&lt;br /&gt;
new Bar(); // выведет &amp;#039;огонь&amp;#039;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Пример виртуальной функции в Delphi ==&lt;br /&gt;
[[Полиморфизм (программирование)|полиморфизм]] языка [[Object Pascal]], использующемся в Delphi . Рассмотрим пример:&lt;br /&gt;
&lt;br /&gt;
Объявим два класса. Предка (Ancestor):&lt;br /&gt;
  TAncestor = &amp;#039;&amp;#039;&amp;#039;class&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;protected&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    {Виртуальная процедура.} &lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; VirtualProcedure&amp;#039;&amp;#039;&amp;#039;; virtual;&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; StaticProcedure&amp;#039;&amp;#039;&amp;#039;;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
и его потомка (Descendant):&lt;br /&gt;
&lt;br /&gt;
  TDescendant = &amp;#039;&amp;#039;&amp;#039;class&amp;#039;&amp;#039;&amp;#039;(TAncestor)&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;protected&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     {Перекрытие виртуальной процедуры.}&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; VirtualProcedure; &amp;#039;&amp;#039;&amp;#039;override;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; StaticProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Как видно в классе предке объявлена виртуальная функция — &amp;lt;code&amp;gt;VirtualProcedure&amp;lt;/code&amp;gt;. Чтобы воспользоваться достоинствами полиморфизма, её нужно [[Перекрытие (программирование)|перекрыть]] в потомке.&lt;br /&gt;
&lt;br /&gt;
Реализация выглядит следующим образом:&lt;br /&gt;
&lt;br /&gt;
  { TAncestor }&lt;br /&gt;
    &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TAncestor.StaticProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    ShowMessage(&amp;#039;Ancestor static procedure.&amp;#039;);&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TAncestor.VirtualProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    ShowMessage(&amp;#039;Ancestor virtual procedure.&amp;#039;);&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
  { TDescendant }&lt;br /&gt;
    &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TDescendant.StaticProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    ShowMessage(&amp;#039;Descendant static procedure.&amp;#039;);&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TDescendant.VirtualProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    ShowMessage(&amp;#039;Descendant override procedure.&amp;#039;);&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Посмотрим как это работает:&lt;br /&gt;
  &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TForm2.BitBtn1Click(Sender: TObject);&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;var&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    MyObject1: TAncestor;&lt;br /&gt;
    MyObject2: TAncestor;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    MyObject1 &amp;#039;&amp;#039;&amp;#039;:=&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;TAncestor&amp;#039;&amp;#039;&amp;#039;.Create;&lt;br /&gt;
    MyObject2 &amp;#039;&amp;#039;&amp;#039;:=&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;TDescendant&amp;#039;&amp;#039;&amp;#039;.Create;&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;try&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
      MyObject1.StaticProcedure;&lt;br /&gt;
      MyObject1.VirtualProcedure;&lt;br /&gt;
      MyObject2.StaticProcedure;&lt;br /&gt;
      MyObject2.VirtualProcedure;&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;finally&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
      MyObject1.Free;&lt;br /&gt;
      MyObject2.Free;&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Заметьте, что в разделе &amp;lt;code&amp;gt;&amp;#039;&amp;#039;&amp;#039;var&amp;#039;&amp;#039;&amp;#039;&amp;lt;/code&amp;gt; мы объявили два объекта &amp;lt;code&amp;gt;MyObject1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;MyObject2&amp;lt;/code&amp;gt; типа &amp;lt;code&amp;gt;TAncestor&amp;lt;/code&amp;gt;. А при создании &amp;lt;code&amp;gt;MyObject1&amp;lt;/code&amp;gt; создали как &amp;lt;code&amp;gt;TAncestor&amp;lt;/code&amp;gt;, а &amp;lt;code&amp;gt;MyObject2&amp;lt;/code&amp;gt; как &amp;lt;code&amp;gt;TDescendant&amp;lt;/code&amp;gt;. Вот что мы увидим при нажатии на кнопку &amp;lt;code&amp;gt;BitBtn1&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Ancestor static procedure.&lt;br /&gt;
# Ancestor virtual procedure.&lt;br /&gt;
# Ancestor static procedure.&lt;br /&gt;
# Descendant override procedure.&lt;br /&gt;
&lt;br /&gt;
Для &amp;lt;code&amp;gt;MyObject1&amp;lt;/code&amp;gt; все понятно, просто вызвались указанные процедуры. А вот для &amp;lt;code&amp;gt;MyObject2&amp;lt;/code&amp;gt; это не так.&lt;br /&gt;
&lt;br /&gt;
Вызов &amp;lt;code&amp;gt;MyObject2.StaticProcedure;&amp;lt;/code&amp;gt; привел к появлению «Ancestor static procedure.». Ведь мы объявили &amp;lt;code&amp;gt;MyObject2: TAncestor&amp;lt;/code&amp;gt;, поэтому и была вызвана процедура &amp;lt;code&amp;gt;StaticProcedure;&amp;lt;/code&amp;gt; класса &amp;lt;code&amp;gt;TAncestor&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
А вот вызов &amp;lt;code&amp;gt;MyObject2.VirtualProcedure;&amp;lt;/code&amp;gt; привел к вызову &amp;lt;code&amp;gt;VirtualProcedure;&amp;lt;/code&amp;gt; реализованной в потомке(&amp;lt;code&amp;gt;TDescendant&amp;lt;/code&amp;gt;). Это произошло потому, что &amp;lt;code&amp;gt;MyObject2&amp;lt;/code&amp;gt; был создан не как &amp;lt;code&amp;gt;TAncestor&amp;lt;/code&amp;gt;, а как &amp;lt;code&amp;gt;TDescendant&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;code&amp;gt;MyObject2 &amp;#039;&amp;#039;&amp;#039;:=&amp;#039;&amp;#039;&amp;#039; TDescendant.Create; &amp;lt;/code&amp;gt;. И виртуальный метод &amp;lt;code&amp;gt;VirtualProcedure&amp;lt;/code&amp;gt; был перекрыт.&lt;br /&gt;
&lt;br /&gt;
В Delphi полиморфизм реализован с помощью так называемой [[Таблица виртуальных методов|виртуальной таблицы методов]] (или VMT).&lt;br /&gt;
&lt;br /&gt;
Достаточно часто виртуальные методы забывают [[перекрытие методов|перекрыть]] с помощью ключевого слова &amp;lt;code&amp;gt;&amp;#039;&amp;#039;&amp;#039;override&amp;#039;&amp;#039;&amp;#039;&amp;lt;/code&amp;gt;. Это приводит к [[закрытие методов|закрытию]] метода. В этом случае замещения методов в VMT не произойдет и требуемая функциональность не будет получена.&lt;br /&gt;
&lt;br /&gt;
Эта ошибка отслеживается компилятором, который выдаёт соответствующее предупреждение.&lt;br /&gt;
&lt;br /&gt;
== Пример виртуального метода на C# ==&lt;br /&gt;
Пример виртуального метода на C#. В примере используется ключевое слово &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;, предоставляющее доступ к методу &amp;lt;code&amp;gt;a()&amp;lt;/code&amp;gt; родительского (базового) класса &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;A&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;.&amp;lt;source lang=&amp;quot;c#&amp;quot;&amp;gt;&lt;br /&gt;
class Program&lt;br /&gt;
{&lt;br /&gt;
    static void Main(string[] args)&lt;br /&gt;
    {&lt;br /&gt;
        A myObj = new B();&lt;br /&gt;
        Console.ReadKey();&lt;br /&gt;
    }        &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Базовый класс A&lt;br /&gt;
public class A&lt;br /&gt;
{&lt;br /&gt;
    public virtual string a()&lt;br /&gt;
    {&lt;br /&gt;
        return &amp;quot;огонь&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Произвольный класс B, наследующий класс A&lt;br /&gt;
class B : A&lt;br /&gt;
{&lt;br /&gt;
    public override string a()&lt;br /&gt;
    {&lt;br /&gt;
        return &amp;quot;вода&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public B()&lt;br /&gt;
    {&lt;br /&gt;
        //Выводим результат возвращаемый переопределённым методом&lt;br /&gt;
        Console.Out.WriteLine(a()); //вода&lt;br /&gt;
        //Выводим результат возвращаемый методом родительского класса&lt;br /&gt;
        Console.Out.WriteLine(base.a());    //огонь&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вызов метода предка из перекрытого метода ==&lt;br /&gt;
Бывает необходимо вызвать метод предка в перекрытом методе.&lt;br /&gt;
&lt;br /&gt;
Объявим два класса. Предка(Ancestor):&lt;br /&gt;
  TAncestor = &amp;#039;&amp;#039;&amp;#039;class&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;protected&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    {Виртуальная процедура.} &lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; VirtualProcedure&amp;#039;&amp;#039;&amp;#039;; virtual;&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
и его потомка (Descendant):&lt;br /&gt;
&lt;br /&gt;
  TDescendant = &amp;#039;&amp;#039;&amp;#039;class&amp;#039;&amp;#039;&amp;#039;(TAncestor)&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;protected&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     {Перекрытие виртуальной процедуры.}&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; VirtualProcedure; &amp;#039;&amp;#039;&amp;#039;override;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Обращение к методу предка реализуется с помощью ключевого слова «inherited»&lt;br /&gt;
&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;procedure&amp;#039;&amp;#039;&amp;#039; TDescendant.VirtualProcedure;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
      &amp;#039;&amp;#039;&amp;#039;inherited;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Стоит помнить, что в Delphi деструктор должен быть обязательно перекрытым — «override» — и содержать вызов деструктора предка&lt;br /&gt;
&lt;br /&gt;
 TDescendant = &amp;#039;&amp;#039;&amp;#039;class&amp;#039;&amp;#039;&amp;#039;(TAncestor)&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;private&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;protected&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;public&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;destructor&amp;#039;&amp;#039;&amp;#039; Destroy; &amp;#039;&amp;#039;&amp;#039;override;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;destructor&amp;#039;&amp;#039;&amp;#039; TDescendant. Destroy;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;begin&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
      &amp;#039;&amp;#039;&amp;#039;inherited;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;end;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
В языке C++ не нужно вызывать конструктор и деструктор предка, деструктор должен быть виртуальным. Деструкторы предков вызовутся автоматически.&lt;br /&gt;
Чтобы вызвать метод предка, нужно явно вызвать метод:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
class Ancestor&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
  virtual void  function1 () { printf(&amp;quot;Ancestor::function1&amp;quot;); }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
class Descendant : public Ancestor&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
  virtual void  function1 () {&lt;br /&gt;
     printf(&amp;quot;Descendant::function1&amp;quot;);&lt;br /&gt;
     Ancestor::function1(); // здесь будет напечатано &amp;quot;Ancestor::function1&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для вызова конструктора предка нужно указать конструктор:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
class Descendant : public Ancestor&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
  Descendant(): Ancestor(){}&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ещё примеры ==&lt;br /&gt;
;Первый пример&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
class Ancestor&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
  virtual void  function1 () { cout &amp;lt;&amp;lt; &amp;quot;Ancestor::function1()&amp;quot; &amp;lt;&amp;lt; endl; }&lt;br /&gt;
  void          function2 () { cout &amp;lt;&amp;lt; &amp;quot;Ancestor::function2()&amp;quot; &amp;lt;&amp;lt; endl; }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
class Descendant : public Ancestor&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
  virtual void  function1 () { cout &amp;lt;&amp;lt; &amp;quot;Descendant::function1()&amp;quot; &amp;lt;&amp;lt; endl; }&lt;br /&gt;
  void          function2 () { cout &amp;lt;&amp;lt; &amp;quot;Descendant::function2()&amp;quot; &amp;lt;&amp;lt; endl; }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Descendant*  pointer      = new Descendant ();&lt;br /&gt;
Ancestor*    pointer_copy = pointer;&lt;br /&gt;
&lt;br /&gt;
pointer-&amp;gt;function1 ();&lt;br /&gt;
pointer-&amp;gt;function2 ();&lt;br /&gt;
&lt;br /&gt;
pointer_copy-&amp;gt;function1 ();&lt;br /&gt;
pointer_copy-&amp;gt;function2 ();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
В этом примере класс &amp;lt;code&amp;gt;Ancestor&amp;lt;/code&amp;gt; определяет две функции, одну из них виртуальную, другую — нет. Класс &amp;lt;code&amp;gt;Descendant&amp;lt;/code&amp;gt; переопределяет обе функции. Однако, казалось бы одинаковое обращение к функциям даёт разные результаты. На выводе программа даст следующее:&lt;br /&gt;
&lt;br /&gt;
     Descendant::function1()&lt;br /&gt;
     Descendant::function2()&lt;br /&gt;
     Descendant::function1()&lt;br /&gt;
     Ancestor::function2()&lt;br /&gt;
&lt;br /&gt;
То есть, для определения реализации виртуальной функции используется информация о типе объекта и вызывается «правильная» реализация, независимо от типа указателя. При вызове невиртуальной функции, [[компилятор]] руководствуется типом указателя или ссылки, поэтому вызываются две разные реализации &amp;lt;code&amp;gt;function2()&amp;lt;/code&amp;gt;, несмотря на то, что используется один и тот же объект.&lt;br /&gt;
&lt;br /&gt;
Следует отметить, что в C++ можно, при необходимости, указать конкретную реализацию виртуальной функции, фактически вызывая её невиртуально:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;pointer-&amp;gt;Ancestor::function1 ();&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
для нашего примера выведет &amp;lt;tt&amp;gt;Ancestor::function1()&amp;lt;/tt&amp;gt;, игнорируя тип объекта.&lt;br /&gt;
&lt;br /&gt;
;Второй пример&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
class A&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
     virtual int function () {&lt;br /&gt;
        return 1;&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     int get() {&lt;br /&gt;
         return this-&amp;gt;function();&lt;br /&gt;
     }&lt;br /&gt;
};&lt;br /&gt;
 &lt;br /&gt;
class B: public A&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
     int function() {&lt;br /&gt;
        return 2;&lt;br /&gt;
     }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
   B b; &lt;br /&gt;
 &lt;br /&gt;
   std::cout &amp;lt;&amp;lt; b.get() &amp;lt;&amp;lt; std::endl; // 2&lt;br /&gt;
   &lt;br /&gt;
   return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что в классе &amp;#039;&amp;#039;B&amp;#039;&amp;#039; отсутствует метод &amp;#039;&amp;#039;get()&amp;#039;&amp;#039;, его можно позаимствовать у класса &amp;#039;&amp;#039;A&amp;#039;&amp;#039;, при этом результат работы этого метода вернет вычисления для &amp;#039;&amp;#039;B::function()&amp;#039;&amp;#039;!&lt;br /&gt;
&lt;br /&gt;
;Третий пример&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
struct IBase&lt;br /&gt;
{&lt;br /&gt;
	virtual void foo(int n=1) const = 0;&lt;br /&gt;
	virtual ~IBase() = 0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void IBase::foo(int n) const {&lt;br /&gt;
	cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &amp;quot; foo\n&amp;quot;;&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
IBase::~IBase() {&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Base destructor\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct Derived final : IBase&lt;br /&gt;
{&lt;br /&gt;
	virtual void foo(int n=2) const override final {&lt;br /&gt;
		IBase::foo(n);&lt;br /&gt;
	}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void bar(const IBase&amp;amp; arg)&lt;br /&gt;
{&lt;br /&gt;
	arg.foo();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main () {&lt;br /&gt;
	bar(Derived());&lt;br /&gt;
	return 0;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В данном примере показан пример создания интерфейса IBase. На примере интерфейса показана возможность создания абстрактного класса, не имеющего виртуальных методов: при объявлении деструктора чистым виртуальным и вынесении его определения за тело класса пропадает возможность создавать объекты такого класса, но остаётся возможность создавать потомки данного предка.&lt;br /&gt;
&lt;br /&gt;
Вывод программы будет следующим: &amp;lt;tt&amp;gt;1 foo\nBase destructor\n&amp;lt;/tt&amp;gt;. Как мы видим, значение аргумента по-умолчанию взялось от типа ссылки, а не от реального типа объекта. Также как и деструктор.&lt;br /&gt;
&lt;br /&gt;
Ключевое слово final показывает, что класс или метод нельзя переопределять, а override, что виртуальный метод явно переопределён.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Объектно-ориентированное программирование]]&lt;br /&gt;
* [[Полиморфизм (информатика)]]&lt;br /&gt;
* [[Абстрактный класс]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
{{примечания}}&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [https://web.archive.org/web/20060429012936/http://www.parashift.com/c++-faq-lite/virtual-functions.html C++ FAQ Lite: Виртуальные функции в C++] {{ref|en}}&lt;br /&gt;
* [https://logic.pdmi.ras.ru/~smal/aptu/cpp10/2010_11_26.html Лекция. Виртуальные функции и полиморфизм] / Конспекты курса по C++ 2010/11, Санкт-Петербургское отделение Математического института им. В.А.Стеклова РАН&lt;br /&gt;
&lt;br /&gt;
[[Категория:Метод (информатика)]]&lt;br /&gt;
[[Категория:Статьи с примерами кода C++]]&lt;/div&gt;</summary>
		<author><name>imported&gt;Alex NB OT</name></author>
	</entry>
</feed>