Какова точка частной чистой виртуальной функции?

Я встретил следующий код в файле заголовка:

class Engine { public: void SetState( int var, bool val ); { SetStateBool( int var, bool val ); } void SetState( int var, int val ); { SetStateInt( int var, int val ); } private: virtual void SetStateBool(int var, bool val ) = 0; virtual void SetStateInt(int var, int val ) = 0; }; 

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

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

Поэтому сначала избавиться от путаницы: Да, частные производные функции могут быть переопределены в производных classах. Методы производных classов не могут вызывать виртуальные функции из базового classа, но они могут обеспечить им собственную реализацию. Согласно Herb Sutter, наличие публичного не виртуального интерфейса в базовом classе и частная реализация, которые могут быть настроены в производных classах, позволяет лучше «отделять спецификацию интерфейса от спецификации настраиваемого поведения реализации». Вы можете больше узнать об этом в своей статье «Виртуальность» .

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

«Общественные перегруженные не виртуальные вызовы защищают неперегруженные виртуальные машины»

Это помогает правильно управлять правилом скрытия . Здесь вы можете узнать больше об этом, но я попытаюсь объяснить это в ближайшее время.

Представьте себе, что виртуальные функции classа Engine также являются его интерфейсом, и это набор перегруженных функций, которые не являются чисто виртуальными. Если бы они были чистыми виртуальными, то все же можно было столкнуться с одной и той же проблемой, как описано ниже, но ниже в иерархии classов.

 class Engine { public: virtual void SetState( int var, bool val ) {/*some implementation*/} virtual void SetState( int var, int val ) {/*some implementation*/} }; 

Теперь предположим, что вы хотите создать производный class, и вам нужно предоставить новую реализацию только для метода, который принимает два ints в качестве аргументов.

 class MyTurbochargedV8 : public Engine { public: // To prevent SetState( int var, bool val ) from the base class, // from being hidden by the new implementation of the other overload (below), // you have to put using declaration in the derived class using Engine::SetState; void SetState( int var, int val ) {/*new implementation*/} }; 

Если вы забыли разместить декларацию использования в производном classе (или переопределить вторую перегрузку), вы можете столкнуться с проблемами в приведенном ниже сценарии.

 MyTurbochargedV8* myV8 = new MyTurbochargedV8(); myV8->SetState(5, true); 

Если вы не предотвратили скрытие членов Engine , заявление:

 myV8->SetState(5, true); 

будет вызывать void SetState( int var, int val ) из производного classа, преобразовывая true в int .

Если интерфейс не является виртуальным, а виртуальная реализация не является общедоступной, как в вашем exmaple, у автора производного classа есть одна проблема, о которой стоит подумать и может просто написать

 class MyTurbochargedV8 : public Engine { private: void SetStateInt(int var, int val ) {/*new implementation*/} }; 

Частная чистая виртуальная функция является базой идиомы не виртуального интерфейса (ОК, это не совсем чисто виртуальный, но все же виртуальный). Конечно, это используется и для других вещей, но я считаю это наиболее полезным (: двумя словами: в публичной функции вы могли бы поместить некоторые общие вещи (например, журнал, статистику и т. Д.) В начале и в конце функции, а затем «посередине» для вызова этой частной виртуальной функции, которая будет отличаться для определенного производного classа. Что-то вроде:

 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() { // .. } 

Чистый виртуальный – просто обязывает производные classы реализовать его.

EDIT : Подробнее об этом: Wikipedia :: NVI-idiom

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

EDIT: Уточненные утверждения о возможности переопределения и возможности доступа / вызова.

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

 #include  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 методы в базовом classе, подобные тем, которые содержатся в вашем коде, обычно используются для реализации шаблона шаблона метода шаблонов . Этот шаблон проектирования позволяет изменить поведение алгоритма в базовом classе без изменения кода в базовом classе. Вышеприведенный код, в котором методы базового classа вызывается с помощью указателя базового classа, является простым примером шаблона метода шаблона.

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

Краткое описание можно найти в DevX.com .


EDIT Частный виртуальный метод эффективно используется в шаблоне метода шаблонов . Производные classы могут переопределять частный виртуальный метод, но производные classы не могут назвать его виртуальным методом базового classа (в вашем примере SetStateBool и SetStateInt ). Только базовый class может эффективно вызывать свой частный виртуальный метод ( только если производным classам необходимо вызвать базовую реализацию виртуальной функции, сделать виртуальную функцию защищенной ).

Интересную статью можно найти о Virtuality .

Interesting Posts

Как работает WOL (Wake-On LAN)?

Что делает тело-парсер с выражением?

Как добавить определенную страницу на страницу «Новая вкладка»?

Почему C # не предоставляет ключевое слово ‘friend’ стиля C ++?

Есть ли способ настроить командную строку командной строки Windows?

Как понять «терминал» директивы?

В форме Django, как сделать поле readonly (или отключено), чтобы он не редактировался?

Что произойдет, если вы добавите графическую карту в i7 со встроенной графикой (например, HD 4000)

Ограничения на hibernate Ограничения И / ИЛИ комбинация

Наблюдение RxJava в параллельных

Как я могу получить доступ к моему мини-компьютеру (RaspberryPi / MK802 / Mele A1000 / VIA APC) через ethernet / wifi без монитора?

Представление больших чисел в исходном коде для удобочитаемости?

Жесткий диск, создающий странный шум

800A0401 – Ожидаемый конец заявления

Когда вы выходите из приложения C, автоматически ли освобождается память malloc-ed?

Давайте будем гением компьютера.