2010-10-19 11 views
108

me encontré con el siguiente código en un archivo de cabecera:¿Cuál es el sentido de una función virtual pura privada?

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 sea la clase Engine o una clase derivada de ella, tiene que proporcionar la implementación de las 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?

Respuesta

170

La pregunta en el tema sugiere una confusión bastante común. La confusión es bastante común, que C++ FAQ abogó contra el uso de virtuales privadas, durante mucho tiempo, porque la confusión parecía ser algo malo.

Para deshacerse primero de la confusión: Sí, las funciones privadas virtuales 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 "Virtuality".

Sin embargo, hay una cosa 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!)

"público sobrecargado no Virtuales de llamadas protegidas con sobrecarga no Virtuales"

Ayuda a properly manage the hiding rule. Puede leer más al respecto here, 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 vamos a suponer que desea crear una clase derivada y es necesario proporcionar una nueva aplicación sólo para el método, que toma dos enteros 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 se olvida de poner la declaración using en la clase derivada (o redefinir el segundo sobrecarga), usted podría tener problemas en el escenario a continuación.

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

Si no impidió que la ocultación de los miembros Engine, la declaración:

myV8->SetState(5, true); 

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

Si la interfaz no es virtual y la aplicación virtual es que no es pública, como en el ejemplo, los grupos el autor de la clase derivada tiene un problema menos para pensar y simplemente puede escribir

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

+1 para "Virtuales no sobrecargados no virtuales con sobrecarga pública no sobrecargados" xD – GabLeRoux

+0

¿por qué la función virtual tiene que ser privada? ¿Puede ser público? – Rich

+0

Me pregunto si las pautas dadas por Herb Sutter en su artículo de "Virtualidad" aún se mantienen hoy en día? – nurabha

14

Bueno, por un lado, esto permitiría a una clase derivada implementar una función que la clase base (que contiene la declaración de función virtual pura) pueda invocar.

+3

que ** solo ** ¡la clase base puede llamar! –

3

El método virtual privado se utiliza 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.

Una breve explicación se puede encontrar en DevX.com.


EDITAR un método virtual privada se utiliza con eficacia en Template Method Pattern. 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, haga que la función virtual esté protegida).

Se puede encontrar un artículo interesante acerca de Virtuality.

+0

@Gentleman ... hmmm baje al comentario de Colin D Bennett. Parece pensar que "una función virtual privada puede ser anulada por clases derivadas, pero solo se puede llamar desde la clase base". @Michael Goldshteyn también piensa así. – BeeBand

+0

Supongo que ha olvidado el principio de que una clase basada en privado no puede ser vista por su clase derivada. Estas son las reglas de OOP y se aplican en todos los idiomas que son OOP. Para que una clase derivada implemente su método virtual privado de clase base, tiene que ser un "amigo" de la clase base. Qt ha seguido el mismo enfoque cuando implementaron su modelo de documento XML DOM. –

+0

@Gentleman: No, no lo he olvidado. Hice un error tipográfico en mi comentario. En lugar de "acceder a los métodos de la clase base", debería haber escrito "puede anular los métodos de la clase base". La clase derivada sin duda puede anular el método de clase base virtual privada, incluso si no puede acceder a ese método de clase base. El artículo de DevX.com que señaló fue incorrecto (herencia pública). Prueba el código en mi respuesta. A pesar del método de clase base virtual privada, la clase derivada puede anularlo. No confundamos la capacidad de anular un método de clase base virtual privada con la capacidad de invocarlo. – Void

36

privada pura función virtual es la base de la no virtual de la interfaz idioma (OK, no es absolutamente siempre pura virtual, pero todavía no virtuales). 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 en el final de la función y, a continuación, "en el medio" para llamar a esta función virtual privada, que será diferente para la clase derivada específica Algo así 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() 
{ 
    // .. 
} 

virtual pura - sólo obliga a la deriva clases para implementarlo

EDITAR:. Más acerca de esto: Wikipedia::NVI-idiom

2

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

Podrá anular esas funciones privadas. Por ejemplo, los siguientes ejemplos ilustrados funcionan (EDIT: método de clase derivada privada y descarta 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 <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); 
} 

Privatevirtual métodos en una clase de base como los de su código se utilizan normalmente para implementar el Template Method design pattern. 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.

+0

Veo, pero si las clases derivadas tienen algún tipo de acceso, ¿por qué molestarse en hacerlas privadas? – BeeBand

+0

@BeeBand: el usuario tendría acceso a las anulaciones de método virtual de clase derivadas públicas, pero no tendría acceso a las de clase base. El autor de la clase derivada en este caso también podría mantener privadas las anulaciones de métodos virtuales. De hecho, haré el cambio en el código de muestra anterior para enfatizar eso. De cualquier forma, siempre podrían heredar públicamente y anular los métodos virtuales de la clase base privada, pero solo tendrían acceso a sus propios métodos virtuales de clase derivados. Tenga en cuenta que estoy haciendo una distinción entre anulación y acceso/invocación. – Void

+0

Interesante. ¿Por qué los downvotes? Si va a votar negativamente, explique por qué en un comentario para que el afiche y otros puedan beneficiarse de su opinión. – Void

Cuestiones relacionadas