¿Cuál es el sentido de una función virtual pura privada?

Encontré el siguiente código en un archivo de encabezado:

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; }; 

Para mí, esto implica que, o bien la clase Engine o una clase derivada de ella, debe proporcionar la implementación de esas funciones virtuales puras. Pero no pensé que las clases derivadas pudieran tener acceso a esas funciones privadas para volver a implementarlas, entonces, ¿por qué hacerlas virtuales?

La pregunta en el tema sugiere una confusión bastante común. La confusión es bastante común, las preguntas frecuentes de C ++ defendían el uso de los servidores virtuales privados, durante mucho tiempo, porque la confusión parecía ser algo malo.

Para deshacerse primero de la confusión: sí, las funciones virtuales privadas pueden anularse en las clases derivadas. Los métodos de clases derivadas no pueden invocar funciones virtuales desde la clase base, pero pueden proporcionarles su propia implementación. Según Herb Sutter, tener una interfaz pública no virtual en la clase base y una implementación privada que se puede personalizar en las clases derivadas, permite una mejor “separación de la especificación de la interfaz de la especificación del comportamiento personalizable de la implementación”. Puede leer más sobre esto en su artículo “Virtualidad” .

Sin embargo, hay algo más interesante en el código que usted presentó, que merece más atención, en mi opinión. La interfaz pública consiste en un conjunto de funciones no virtuales sobrecargadas y esas funciones llaman a funciones virtuales no públicas y no sobrecargadas. Como es habitual en el mundo de C ++, es un modismo, tiene un nombre y, por supuesto, es útil. El nombre es (sorpresa, sorpresa!)

“Virtuales no virtuales sobrecargados públicos llaman a los virtuales no sobrecargados protegidos”

Ayuda a administrar adecuadamente la regla de ocultamiento . Puede leer más sobre esto aquí , pero trataré de explicarlo en breve.

Imagine que las funciones virtuales de la clase Engine también son su interfaz y es un conjunto de funciones sobrecargadas que no es puramente virtual. Si fueran virtuales puros, uno podría encontrar el mismo problema, como se describe a continuación, pero inferior en la jerarquía de clases.

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

Ahora supongamos que desea crear una clase derivada y necesita proporcionar una nueva implementación solo para el método, que toma dos ints como argumentos.

 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*/} }; 

Si olvidó colocar la statement de uso en la clase derivada (o redefinir la segunda sobrecarga), podría tener problemas en el siguiente escenario.

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

Si no evitó la ocultación de los miembros del Engine , la statement:

 myV8->SetState(5, true); 

llamaría a void SetState( int var, int val ) de la clase derivada, convirtiendo true en int .

Si la interfaz no es virtual y la implementación virtual no es pública, como en su ejemplo, el autor de la clase derivada tiene un problema menor en el que pensar y simplemente puede escribir

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

La función virtual privada pura es la base de la expresión de la interfaz no virtual (OK, no es absolutamente siempre puramente virtual, pero sigue siendo virtual allí). Por supuesto, esto se usa también para otras cosas, pero esto me parece muy útil (: en dos palabras: en una función pública, podrías poner algunas cosas en común (como registro, estadísticas, etc.) al principio y al final de la función y luego, “en el medio” para llamar a esta función privada virtual, eso será diferente para la clase derivada específica. Algo como:

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

Pure virtual : solo obliga a las clases derivadas a implementarlo.

EDITAR : Más sobre esto: Wikipedia :: NVI-idiom

Bueno, para empezar, esto permitiría a una clase derivada implementar una función que la clase base (que contiene la statement de la función virtual pura) pueda invocar.

EDITAR: declaraciones aclaradas sobre la capacidad de anular y la capacidad de acceder / invocar.

Podrá anular esas funciones privadas. Por ejemplo, el siguiente ejemplo inventado funciona ( EDITAR: hizo que el método de clase derivada sea privado y omita la invocación del método de clase derivada en main() para demostrar mejor la intención del patrón de diseño en uso ):

 #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 métodos virtual Private en una clase base como los de su código se usan generalmente para implementar el patrón de diseño de Método de plantilla . Ese patrón de diseño permite cambiar el comportamiento de un algoritmo en la clase base sin cambiar el código en la clase base. El código anterior en el que se invocan los métodos de clase base a través de un puntero de clase base es un ejemplo simple del patrón de Método de plantilla.

El método virtual privado se usa para limitar el número de clases derivadas que pueden anular la función dada. Las clases derivadas que tienen que anular el método virtual privado tendrán que ser un amigo de la clase base.

Se puede encontrar una breve explicación de DevX.com .


EDITAR Un método virtual privado se usa efectivamente en Patrón de método de plantilla . Las clases derivadas pueden anular el método virtual privado pero las clases derivadas no pueden llamarlo método virtual privado de la clase base (en su ejemplo, SetStateBool y SetStateInt ). Solo la clase base puede llamar efectivamente a su método virtual privado ( solo si las clases derivadas necesitan invocar la implementación base de una función virtual, hacer que la función virtual esté protegida ).

Se puede encontrar un interesante artículo sobre Virtuality .