Вопрос в теме наводит на довольно распространенную путаницу. Путаница довольно распространена, поэтому C ++ FAQ долгое время выступал против использования частных виртуальных машин, потому что путаница казалась плохой вещью.
Итак, чтобы сначала избавиться от путаницы: да, частные виртуальные функции могут быть переопределены в производных классах. Методы производных классов не могут вызывать виртуальные функции из базового класса, но могут предоставить для них собственную реализацию. По словам Херба Саттера, наличие общедоступного невиртуального интерфейса в базовом классе и частной реализации, которая может быть настроена в производных классах, позволяет лучше «отделить спецификацию интерфейса от спецификации настраиваемого поведения реализации». Подробнее об этом читайте в его статье «Виртуальность» .
Однако в представленном вами коде есть еще одна интересная вещь, которая, на мой взгляд, заслуживает большего внимания. Открытый интерфейс состоит из набора перегруженных невиртуальных функций, и эти функции вызывают непубличные, неперегруженные виртуальные функции. Как обычно в мире C ++, это идиома, у нее есть имя и, конечно же, она полезна. Имя (сюрприз, сюрприз!)
«Общедоступные перегруженные невиртуальные виртуальные машины, защищенные вызовом неперегруженных виртуальных машин»
Это помогает правильно управлять правилом сокрытия . Вы можете прочитать об этом здесь , но я постараюсь объяснить это в ближайшее время.
Представьте себе, что виртуальные функции Engine
класса также являются его интерфейсом и представляют собой набор перегруженных функций, которые не являются чисто виртуальными. Если бы они были чисто виртуальными, можно было бы столкнуться с той же проблемой, как описано ниже, но ниже в иерархии классов.
class Engine
{
public:
virtual void SetState( int var, bool val ) {}
virtual void SetState( int var, int val ) {}
};
Теперь предположим, что вы хотите создать производный класс и вам нужно предоставить новую реализацию только для метода, который принимает два целых числа в качестве аргументов.
class MyTurbochargedV8 : public Engine
{
public:
using Engine::SetState;
void SetState( int var, int val ) {}
};
Если вы забыли поместить объявление using в производный класс (или переопределить вторую перегрузку), у вас могут возникнуть проблемы в сценарии ниже.
MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
Если вы не предотвратили сокрытие Engine
участников, утверждение:
myV8->SetState(5, true);
будет вызывать void SetState( int var, int val )
из производного класса, преобразовывая true
в int
.
Если интерфейс не виртуальный и виртуальная реализация не является общедоступной, как в вашем примере, у автора производного класса на одну проблему меньше, и он может просто написать
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {}
};