С определением структуры, приведенным ниже ...
struct A {
virtual void hello() = 0;
};
Подход № 1:
struct B : public A {
virtual void hello() { ... }
};
Подход № 2:
struct B : public A {
void hello() { ... }
};
Есть ли разница между этими двумя способами переопределения функции hello?
c++
overriding
virtual-functions
Anarki
источник
источник
Ответы:
Они точно такие же. Между ними нет никакой разницы, кроме того, что первый подход требует большего набора текста и потенциально более ясен.
источник
override
ключевое слово.«Виртуальность» функции распространяется неявно, однако, по крайней мере, один используемый мной компилятор сгенерирует предупреждение, если
virtual
ключевое слово не используется явно, поэтому вы можете использовать его, если только для того, чтобы компилятор не работал.С чисто стилистической точки зрения включение
virtual
ключевого слова явно «рекламирует» тот факт, что функция является виртуальной. Это будет важно для любого дальнейшего подкласса B без необходимости проверять определение A. Для глубокой иерархии классов это становится особенно важным.источник
virtual
Ключевое слово не является необходимым в производном классе. Вот подтверждающая документация из проекта стандарта C ++ (N3337) (выделено мной):источник
Нет,
virtual
ключевое слово в переопределениях виртуальных функций производных классов не требуется. Но стоит упомянуть связанную с этим ловушку: неспособность переопределить виртуальную функцию.Отказ переопределение происходит , если вы собираетесь переопределить виртуальную функцию в производном классе, но сделать ошибку в подписи так , что он объявляет новую и другую виртуальную функцию. Эта функция может быть перегружена функцией базового класса или может отличаться по имени. Независимо от того, используете ли вы
virtual
ключевое слово в объявлении функции производного класса, компилятор не сможет сказать, что вы намеревались переопределить функцию из базового класса.Однако эта ловушка, к счастью, устранена функцией явного переопределения языка C ++ 11 , которая позволяет исходному коду четко указать, что функция-член предназначена для переопределения функции базового класса:
Компилятор выдаст ошибку времени компиляции, и ошибка программирования будет сразу же очевидна (возможно, функция в Derived должна была принять
float
аргумент a ).Обратитесь к WP: C ++ 11 .
источник
Добавление «виртуального» ключевого слова является хорошей практикой, поскольку оно улучшает читабельность, но это не обязательно. Функции, объявленные виртуальными в базовом классе и имеющие одинаковую подпись в производных классах, по умолчанию считаются «виртуальными».
источник
Для компилятора нет разницы, когда вы пишете
virtual
в производном классе или опускаете его.Но вам нужно взглянуть на базовый класс, чтобы получить эту информацию. Поэтому я бы рекомендовал добавить
virtual
ключевое слово также в производный класс, если вы хотите показать человеку, что эта функция виртуальная.источник
Существует значительная разница, когда у вас есть шаблоны и вы начинаете принимать базовый класс (ы) в качестве параметров шаблона:
Самое интересное в том, что теперь вы можете определять интерфейсные и неинтерфейсные функции позже, чем определять классы. Это полезно для взаимодействия интерфейсов между библиотеками (не полагайтесь на это как на стандартный процесс проектирования отдельной библиотеки). Это ничего не стоит, чтобы позволить это для всех ваших классов - вы можете даже
typedef
B к чему-то, если хотите.Обратите внимание, что если вы сделаете это, вы можете также объявить конструкторы копирования / перемещения в качестве шаблонов: разрешение конструировать из разных интерфейсов позволяет вам «приводить» между разными
B<>
типами.Сомнительно, стоит ли вам добавлять поддержку
const A&
вt_hello()
. Обычная причина такого переписывания - перейти от специализации на основе наследования к специализации на основе шаблонов, в основном из соображений производительности. Если вы продолжаете поддерживать старый интерфейс, вы вряд ли сможете обнаружить (или предотвратить) старое использование.источник
virtual
Ключевое слово должно быть добавлено к функциям базового класса , чтобы сделать их переопределение. В вашем примереstruct A
это базовый класс.virtual
ничего не значит для использования этих функций в производном классе. Тем не менее, если вы хотите, чтобы ваш производный класс также являлся самим базовым классом, и вы хотите, чтобы эта функция была перезаписываемой, тогда вам придется поместитьvirtual
туда.Здесь
C
наследуетсяB
, поэтомуB
не является базовым классом (это также производный класс), аC
является производным классом. Диаграмма наследования выглядит так:Таким образом, вы должны поставить
virtual
перед функциями внутри потенциальных базовых классов, которые могут иметь детей.virtual
позволяет вашим детям отменять ваши функции. Существует ничего плохого поставивvirtual
перед функциями внутри из производных классов, но это не требуется. Тем не менее, рекомендуется, потому что если кто-то захочет наследовать от вашего производного класса, он не будет рад, что переопределение метода не работает, как ожидалось.Поэтому ставьте
virtual
перед функциями все классы, участвующие в наследовании, если только вы точно не знаете, что в классе не будет детей, которым нужно было бы переопределять функции базового класса. Это хорошая практика.источник
Я обязательно включу ключевое слово Virtual для дочернего класса, потому что
источник