Вопрос в теме наводит на довольно распространенную путаницу. Путаница довольно распространена, поэтому 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 ) {}
};
Частная чистая виртуальная функция является основой идиомы невиртуального интерфейса (хорошо, это не всегда чисто виртуальный, но все же виртуальный там). Конечно, это используется и для других целей, но я считаю это наиболее полезным (: в двух словах: в публичной функции вы можете поместить некоторые общие вещи (такие как ведение журнала, статистика и т. Д.) В начало и в конце функции, а затем «посередине» для вызова этой частной виртуальной функции, которая будет отличаться для конкретного производного класса. Примерно так:
class Base { // .. public: void f(); private: virtual void DerivedClassSpecific() = 0; // .. }; void Base::f() { //.. Do some common stuff DerivedClassSpecific(); //.. Some other common stuff } // .. class Derived: public Base { // .. private: virtual void DerivedClassSpecific(); //.. }; void Derived::DerivedClassSpecific() { // .. }
Чисто виртуальный - просто обязывает производные классы реализовать его.
РЕДАКТИРОВАТЬ : Подробнее об этом: Википедия :: NVI-idiom
источник
Ну, во-первых, это позволило бы производному классу реализовать функцию, которую может вызывать базовый класс (содержащий объявление чистой виртуальной функции).
источник
РЕДАКТИРОВАТЬ: Уточнены утверждения о возможности переопределения и возможности доступа / вызова.
Он сможет переопределить эти частные функции. Например, работает следующий надуманный пример ( EDIT: сделал метод производного класса закрытым и отбросил вызов метода производного класса,
main()
чтобы лучше продемонстрировать цель использования шаблона проектирования ):#include <iostream> class Engine { public: void SetState( int var, bool val ) { SetStateBool( var, val ); } void SetState( int var, int val ) { SetStateInt( var, val ); } private: virtual void SetStateBool(int var, bool val ) = 0; virtual void SetStateInt(int var, int val ) = 0; }; class DerivedEngine : public Engine { private: virtual void SetStateBool(int var, bool val ) { std::cout << "DerivedEngine::SetStateBool() called" << std::endl; } virtual void SetStateInt(int var, int val ) { std::cout << "DerivedEngine::SetStateInt() called" << std::endl; } }; int main() { DerivedEngine e; Engine * be = &e; be->SetState(4, true); be->SetState(2, 1000); }
Private
virtual
Методы базового класса, подобные тем, которые содержатся в вашем коде, обычно используются для реализации шаблона проектирования Template Method . Этот шаблон проектирования позволяет изменять поведение алгоритма в базовом классе без изменения кода в базовом классе. Приведенный выше код, в котором методы базового класса вызываются через указатель базового класса, является простым примером шаблона метода шаблона.источник
Engine
и неDerivedEngine
имеет ничего общего с тем, чтоDerivedEngine
можно или нельзя переопределить (или получить доступ, если на то пошло).Частный виртуальный метод используется для ограничения количества производных классов, которые могут переопределять данную функцию. Производные классы, которые должны переопределять частный виртуальный метод, должны быть друзьями базового класса.
Краткое объяснение можно найти на DevX.com .
РЕДАКТИРОВАТЬ Частный виртуальный метод эффективно используется в шаблоне метода шаблона . Производные классы могут переопределять частный виртуальный метод, но производные классы не могут вызывать его частный виртуальный метод базового класса (в вашем примере
SetStateBool
иSetStateInt
). Только базовый класс может эффективно вызывать свой частный виртуальный метод ( только если производным классам необходимо вызывать базовую реализацию виртуальной функции, сделайте виртуальную функцию защищенной ).Можно найти интересную статью о виртуальности .
источник
friend
из базовых классов. Qt применила тот же подход, когда реализовала свою модель документа XML DOM.TL; DR ответ:
Вы можете относиться к нему как к другому уровню инкапсуляции - где-то между защищенным и закрытым : вы не можете вызывать его из дочернего класса, но вы можете его переопределить.
Это полезно при реализации шаблона проектирования " Шаблонный метод" . Вы можете использовать защищенный , но частный вместе с виртуальным может считаться лучшим выбором из-за лучшей инкапсуляции.
источник