Virtual void c что это
Виртуальные функции
Виртуальные функции — специальный вид функций-членов класса. Виртуальная функция отличается об обычной функции тем, что для обычной функции связывание вызова функции с ее определением осуществляется на этапе компиляции. Для виртуальных функций это происходит во время выполнения программы.
Виртуальная функция — это функция, которая определяется в базовом классе, а любой порожденный класс может ее переопределить. Виртуальная функция вызывается только через указатель или ссылку на базовый класс.
Указатель на базовый класс может указывать либо на объект базового класса, либо на объект порожденного класса. Выбор функции-члена зависит от того, на объект какого класса при выполнении программы указывает указатель, но не от типа указателя. При отсутствии члена порожденного класса по умолчанию используется виртуальная функция базового класса.
Результат выполнения
В терминологии ООП «объект посылает сообщение print и выбирает свою собственную версию соответствующего метода». Виртуальной может быть только нестатическая функция-член класса. Для порожденного класса функция автоматически становится виртуальной, поэтому ключевое слово virtual можно опустить.
Пример : выбор виртуальной функции
Результат выполнения
Чистая виртуальная функция
Чистая виртуальная функция — это метод класса, тело которого не определено.
В базовом классе такая функция записывается следующим образом:
Для рассмотренного выше примера (класс Фигура) функцию вычисления площади целесообразно задать чистой виртуальной функцией, которую переопределяет каждый наследуемый класс.
Строка 9 при этом будет иметь вид:
virtual (Справочник по C#)
Ключевое слово virtual используется для изменения объявлений методов, свойств, индексаторов и событий и разрешения их переопределения в производном классе. Например, этот метод может быть переопределен любым наследующим его классом:
Реализацию виртуального члена можно изменить путем переопределения члена в производном классе. Дополнительные сведения об использовании ключевого слова virtual см. в разделах Управление версиями с помощью ключевых слов Override и New и Использование ключевых слов Override и New.
Remarks
При вызове виртуального метода тип времени выполнения объекта проверяется на переопределение члена. Вызывается переопределение члена в самом дальнем классе. Это может быть исходный член, если никакой производный класс не выполнял переопределение этого члена.
По умолчанию методы не являются виртуальными. Такой метод переопределить невозможно.
Действие виртуальных свойств аналогично виртуальным методам, за исключением отличий в синтаксисе объявлений и вызовов.
Использование модификатора virtual в статическом свойстве является недопустимым.
Пример
Следующая программа вычисляет и отображает соответствующую область для каждой фигуры путем вызова нужной реализации метода Area() в соответствии с объектом, связанным с методом.
Спецификация языка C#
Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
Виртуальные функции
Виртуальная функция — это функция-член, которую предполагается переопределить в производных классах. При ссылке на объект производного класса с помощью указателя или ссылки на базовый класс можно вызвать виртуальную функцию для этого объекта и выполнить версию функции производного класса.
Виртуальные функции обеспечивают вызов соответствующей функции для объекта независимо от выражения, используемого для вызова функции.
Функции в производных классах переопределяют виртуальные функции в базовых классах, только если их тип совпадает. Функция в производном классе не может отличаться от виртуальной функции в базовом классе только возвращаемым типом; список аргументов также должен отличаться.
При вызове функции с помощью указателей или ссылок применяются следующие правила.
Вызов виртуальной функции разрешается в соответствии с базовым типом объекта, для которого она вызывается.
Вызов невиртуальной функции разрешается в соответствии с типом указателя или ссылки.
В следующем примере показано поведение виртуальной и невиртуальной функций при вызове с помощью указателей.
virtual Ключевое слово можно использовать при объявлении переопределяемых функций в производном классе, но это не обязательно; переопределения виртуальных функций всегда являются виртуальными.
Виртуальные функции в базовом классе должны быть определены, если они не объявлены с помощью чистого описателя. (Дополнительные сведения о чистых виртуальных функциях см. в разделе абстрактные классы.)
Оба вызова PrintBalance в предыдущем примере подавляют механизм вызова виртуальных функций.
Виртуальные функции в C++
В этом руководстве мы узнаем о виртуальной функции C++ и ее использовании с помощью примеров.
Что такое виртуальная функция в C++?
Виртуальная функция в C++ – это функция-член в базовом классе, которую мы ожидаем переопределить в производных классах.
Виртуальную функцию в C++ используют в базовом классе, чтобы гарантировать, что функция переопределена. Это относится к случаям, когда указатель базового класса указывает на объект производного класса.
Например, рассмотрим код ниже:
Позже, если мы создадим указатель базового типа для указания на объект производного класса и вызовем функцию print(), он вызовет функцию print() базового класса.
Другими словами, функция-член Base не переопределяется.
Чтобы этого избежать, мы объявляем функцию print() базового класса виртуальной с помощью ключевого слова virtual.
Виртуальные функции являются неотъемлемой частью полиморфизма в С++.
Пример 1
Здесь мы объявили функцию print() в Base, как виртуальную.
Идентификатор переопределения
C++ 11 дал нам новое переопределение идентификатора, которое очень полезно, во избежание ошибок при использовании виртуальных функций.
Этот идентификатор определяет функции-члены производных классов, которые переопределяют функцию-член базового класса.
Если мы используем прототип функции в классе Derived и определяем эту функцию вне класса, то мы используем следующий код:
Использование переопределения
При использовании виртуальных функций в си++ при объявлении функций-членов производных классов можно сделать ошибки.
Использование идентификатора переопределения заставляет компилятор отображать сообщения об ошибках, когда эти ошибки сделаны.
В противном случае программа просто скомпилируется, но виртуальная функция не будет отменена.
Вот некоторые из этих возможных ошибок:
Использование функции
У нас есть базовый класс Animal и производные классы Dog и Cat.
Теперь предположим, что наша программа требует от нас создания двух общедоступных функций для каждого класса:
Мы могли бы создать обе эти функции в каждом классе отдельно и переопределить их, что будет долгим и утомительным процессом.
Или мы могли бы сделать getType() виртуальным в классе Animal, а затем создать одну отдельную функцию print(), которая принимает указатель типа Animal в качестве аргумента. Затем мы можем использовать эту единственную функцию, чтобы переопределить виртуальную.
Это сделает код короче, чище и менее повторяющимся.
Пример 2: демонстрация
В main() мы создали 3 указателя Animal для динамического создания объектов классов Animal, Dog и Cat.
Затем мы вызываем функцию print(), используя эти указатели:
Урок №163. Виртуальные функции и Полиморфизм
Обновл. 15 Сен 2021 |
На предыдущем уроке мы рассматривали ряд примеров, в которых использование указателей или ссылок родительского класса упрощало логику и уменьшало количество кода.
Виртуальные функции и Полиморфизм
Тем не менее, мы сталкивались с проблемой, когда родительский указатель или ссылка вызывали только родительские методы, а не дочерние. Например:
rParent is a Parent
На этом уроке мы рассмотрим, как можно решить эту проблему с помощью виртуальных функций.
Виртуальная функция в языке С++ — это особый тип функции, которая, при её вызове, выполняет «наиболее» дочерний метод, который существует между родительским и дочерними классами. Это свойство еще известно, как полиморфизм. Дочерний метод вызывается тогда, когда совпадает сигнатура (имя, типы параметров и является ли метод константным) и тип возврата дочернего метода с сигнатурой и типом возврата метода родительского класса. Такие методы называются переопределениями (или «переопределенными методами»).
Чтобы сделать функцию виртуальной, нужно просто указать ключевое слово virtual перед объявлением функции. Например:
rParent is a Child
Рассмотрим пример посложнее:
Как вы думаете, какой результат выполнения этой программы?
Рассмотрим всё по порядку:
Сначала создается объект c класса C.
Вызов rParent.GetName() приводит к вызову A::getName(). Однако, поскольку A::getName() является виртуальной функцией, то компилятор ищет «наиболее» дочерний метод между A и C. В этом случае — это C::getName().
Обратите внимание, компилятор не будет вызывать D::getName(), поскольку наш исходный объект был класса C, а не класса D, поэтому рассматриваются методы только между классами A и C.
Результат выполнения программы:
Более сложный пример
Рассмотрим класс Animal из предыдущего урока, добавив тестовый код:
Результат выполнения программы:
А теперь рассмотрим тот же класс, но сделав метод speak() виртуальным:
Результат выполнения программы:
Matros says Meow
Barsik says Woof
Обратите внимание, мы не сделали Animal::GetName() виртуальной функцией. Это из-за того, что GetName() никогда не переопределяется ни в одном из дочерних классов, поэтому в этом нет необходимости.
Аналогично со следующим примером с массивом животных:
Matros says Meow
Barsik says Woof
Ivan says Meow
Tolik says Woof
Martun says Meow
Tyzik says Woof
Несмотря на то, что эти два примера используют только классы Cat и Dog, любые другие дочерние классы также будут работать с нашей функцией report() и с массивом животных, без внесения дополнительных модификаций! Это, пожалуй, самое большое преимущество виртуальных функций — возможность структурировать код таким образом, чтобы новые дочерние классы автоматически работали со старым кодом, без необходимости внесения изменений со стороны программиста!
Предупреждение: Сигнатура виртуального метода дочернего класса должна полностью соответствовать сигнатуре виртуального метода родительского класса. Если у дочернего метода будет другой тип параметров, нежели у родительского, то вызываться этот метод не будет.
Использование ключевого слова virtual
Если функция отмечена как виртуальная, то все соответствующие переопределения тоже считаются виртуальными, даже если возле них явно не указано ключевое слова virtual. Однако, наличие ключевого слова virtual возле методов дочерних классов послужит полезным напоминанием о том, что эти методы являются виртуальными, а не обычными. Следовательно, полезно указывать ключевое слово virtual возле переопределений в дочерних классах, даже если это не является строго необходимым.
Типы возврата виртуальных функций
Типы возврата виртуальной функции и её переопределений должны совпадать. Рассмотрим следующий пример:
В этом случае Child::getValue() не считается подходящим переопределением для Parent::getValue(), так как типы возвратов разные (метод Child::getValue() считается полностью отдельной функцией).
Не вызывайте виртуальные функции в теле конструкторов или деструкторов
Вот еще одна ловушка для новичков. Вы не должны вызывать виртуальные функции в теле конструкторов или деструкторов. Почему?
Помните, что при создании объекта класса Child сначала создается родительская часть этого объекта, а затем уже дочерняя? Если вы будете вызывать виртуальную функцию из конструктора класса Parent при том, что дочерняя часть создаваемого объекта еще не была создана, то вызвать дочерний метод вместо родительского будет невозможно, так как объект child для работы с методом класса Child еще не будет создан. В таких случаях, в языке C++ будет вызываться родительская версия метода.
Аналогичная проблема существует и с деструкторами. Если вы вызываете виртуальную функцию в теле деструктора класса Parent, то всегда будет вызываться метод класса Parent, так как дочерняя часть объекта уже будет уничтожена.
Правило: Никогда не вызывайте виртуальные функции в теле конструкторов или деструкторов.
Недостаток виртуальных функций
«Если всё так хорошо с виртуальными функциями, то почему бы не сделать все методы виртуальными?» — спросите Вы. Ответ: «Это неэффективно!». Обработка и выполнение вызова виртуального метода занимает больше времени, чем обработка и выполнение вызова обычного метода. Кроме того, компилятор также должен выделять один дополнительный указатель для каждого объекта класса, который имеет одну или несколько виртуальных функций.
Какой результат выполнения следующих программ? Не нужно запускать/выполнять следующий код, вы должны определить результат, без помощи своих IDE.