Когда целесообразно использовать виртуальные методы? - PullRequest
20 голосов
/ 19 мая 2011

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

Ответы [ 10 ]

9 голосов
/ 19 мая 2011

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

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

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

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

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

5 голосов
/ 19 мая 2011

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

  1. Пока вам не нужно наследовать от класса, не пишите метод virtual, если вам нужно извлечь, сделайте virtual только те методы, которые вам нужно настроить в дочернем классе.
  2. Если у класса есть метод virtual, то для деструктора будет virtual (конец обсуждения).
  3. Попытайтесь следовать идиоме NVI (Non-Virtual Interface), сделайте метод virtual закрытым и предоставьте общедоступные оболочки, отвечающие за оценку условий до и после, чтобы производные классы не могли случайно их нарушить.

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

3 голосов
/ 19 мая 2011

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

3 голосов
/ 19 мая 2011

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

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

2 голосов
/ 19 мая 2011

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

1 голос
/ 19 мая 2011

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

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

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

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

0 голосов
/ 19 мая 2011

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

0 голосов
/ 19 мая 2011

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

0 голосов
/ 19 мая 2011

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

...