Подтвердить что ты не робот

Когда целесообразно использовать виртуальные методы?

Я понимаю, что виртуальные методы позволяют производному классу переопределять методы, унаследованные от базового класса. Когда подходит или неуместно использовать виртуальные методы? Не всегда известно, будет ли класс подклассифицирован. Если все станет виртуальным, просто "на случай"? Или это вызовет значительные накладные расходы?

4b9b3361

Ответ 1

Когда вы создаете класс, у вас должна быть довольно хорошая идея относительно того, представляет ли он интерфейс (в этом случае вы помещаете соответствующие переопределяемые методы и виртуальный деструктор) ИЛИ он предназначен для использования как есть, возможно составлен или составлен с другими объектами.

Другими словами, ваше намерение для класса должно быть вашим руководством. Создание всего виртуального часто бывает излишним и иногда вводит в заблуждение относительно того, какие методы предназначены для поддержки полиморфизма времени выполнения.

Ответ 2

Сначала небольшое педантичное замечание - в стандарте C++ мы называем их функциями-членами, а не методами, хотя эти два термина эквивалентны.

Я вижу две причины НЕ делать функцию-член виртуальной.

  • "ЯГНИ" - "Вам это не нужно". Если вы не уверены, что класс будет производным, предположите, что это не так, и не делайте функции-члены виртуальными. Ничто так не говорит "не производи от меня", как не виртуальный деструктор (правка: в C++ 11 и выше у тебя есть final ключевое слово], что даже лучше). Это также о намерениях. Если вы не собираетесь использовать класс полиморфно, не создавайте ничего виртуального. Если вы произвольно делаете членов виртуальными, вы предлагаете злоупотребления Принципом подстановки Лискова, и эти классы ошибок трудно отследить и устранить.
  • Производительность/объем памяти. Класс, не имеющий виртуальных функций-членов, не требует VTable (виртуальной таблицы, используемой для перенаправления полиморфных вызовов через указатель базового класса) и, таким образом (потенциально), занимает меньше места в памяти. Кроме того, прямой вызов функции-члена (потенциально) быстрее вызова виртуальной функции-члена. Не преждевременно пессимизируйте ваш класс, превратив превентивные функции-члены в виртуальные.

Ответ 3

Это сложный вопрос. Но есть некоторые рекомендации/правило, которым нужно следовать.

  • До тех пор, пока вам не нужно выводить из класса, тогда не следует писать какой-либо метод virtual, как только вам нужно получить, только сделайте virtual те методы, которые вам нужно настроить в дочернем классе.
  • Если класс имеет метод virtual, деструктор должен быть virtual (конец обсуждения).
  • Попробуйте следовать идиоме NVI (не виртуального интерфейса), сделайте метод virtual непубличным и предоставите общедоступные обертки, отвечающие за оценку условий pre и post, чтобы производные классы не могли случайно их разбить.

Я думаю, что они достаточно просты. Я определенно позволяю ABI часть отражения, это полезно только при доставке DLL.

Ответ 4

Если ваш код соответствует определенному шаблону проектирования, ваш выбор должен отражать собственные принципы DP. Например, если вы кодируете шаблон Decorator, то функция, которая должна быть виртуальной, - это те, которые принадлежат интерфейсу Component.

В противном случае я хотел бы следовать эволюционному подходу, IOW У меня нет виртуальных методов, пока я не увижу, что иерархия пытается выйти из вашего кода.

Ответ 5

Например, функции-члены в Java на 100% виртуальны. В С++ это рассматривается как штраф за размер кода/время вызова функции. Кроме того, не виртуальная функция гарантирует, что реализация функции всегда будет одинаковой (с использованием объекта/ссылки базового класса). Скотт Майерс в "Эффективном С++" обсуждает его более подробно.

Ответ 6

Тест на здравомыслие, который я использую в основном: - Если класс, который я определяю, происходит в будущем, будет ли поведение (функция) оставаться таким же или было бы необходимо переопределить. Если бы это было так, функция была бы сильным соперником за то, что она была виртуальной, если нет, то нет, если я не знаю - мне, вероятно, нужно заглянуть в проблемную область, чтобы лучше понять поведение, которое я планирую реализовать. В основном проблемный домен дает мне ответ - в тех случаях, когда это поведение вообще не критично.

Ответ 7

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

Одним из тривиальных примеров может быть проблема вычисления областей для различных геометрических фигур. Вам нужны области квадратов, кругов, прямоугольников, треугольников и т.д., И единственное, что здесь меняется, это математические формулы (способ), которые вы используете для вычисления области. Поэтому было бы правильным решением, чтобы каждая из этих форм наследовалась от общего базового класса и добавляла виртуальный метод в базовый класс, который возвращает область (которую вы затем можете реализовать в каждом дочернем элементе с соответствующей математической формулой).

Создание виртуального "на всякий случай" приведет к тому, что ваши объекты занимают больше памяти. Кроме того, при вызове виртуальных функций существует небольшая (но ненулевая) накладная. Таким образом, IMHO, делая все виртуальным "на всякий случай" будет плохой идеей, когда важны ограничения производительности/памяти (что в основном означает всегда в каждой реальной программе, которую вы пишете).

Однако это опять-таки дискуссионно, исходя из того, насколько четко изложены требования и как часто ожидаются изменения кода. Например, в быстром и грязном инструменте или в исходном прототипе, где несколько лишних байтов памяти и несколько миллисекунд потерянного времени не означают многого, было бы нормально иметь кучу (без необходимости) виртуальных функций ради гибкости.

Ответ 8

Моя точка зрения заключается в том, что если вы хотите использовать указатель родительского класса для указания экземпляра дочернего класса и использовать их методы, тогда вы должны использовать виртуальные методы.

Ответ 9

Виртуальные методы - это способ достижения полиморфизма. Они используются, когда вы хотите определить какое-либо действие на более абстрактном уровне, так что его невозможно реализовать, потому что он слишком общий. Только в производных классах вы можете сказать, как выполнить это действие. Но с определением виртуального метода вы создаете требование, которое добавляет жесткость иерархии классов. Это может быть целесообразным или нет, это зависит от того, что вы пытаетесь получить, и по своему вкусу.

Ответ 10

Посмотрите Шаблоны проектирования. Если ваш код/​​дизайн является одним из этих или подобных, используйте виртуальную функцию. В противном случае попробуйте this