2010-01-31 19 views
81

¿Cuál es la ventaja de hacer que un método privado sea virtual en C++?Método virtual privado en C++

he dado cuenta de esto en una fuente en C++ proyecto abierto:

class HTMLDocument : public Document, public CachedResourceClient { 
private: 
    virtual bool childAllowed(Node*); 
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&); 
}; 
+5

Creo que la pregunta es al revés. La razón para hacer algo virtual es siempre la misma: permitir que las clases derivadas lo anulen. Entonces la pregunta debería ser: ¿cuál es la ventaja de hacer que un método virtual sea privado? A lo que la respuesta es: hacer que todo sea privado por defecto. :-) – ShreevatsaR

+0

@ShreevatsaR Pero ni siquiera respondió su propia pregunta ...... – Spencer

Respuesta

92

Herb Sutter ha explicado muy bien que here.

Guía n.º 2: Prefiere hacer que las funciones virtuales sean privadas.

Esto permite que las clases derivadas anulen la función para personalizar el comportamiento según sea necesario, sin exponer las funciones virtuales directamente haciéndolas invocables por clases derivadas (como sería posible si las funciones solo estuvieran protegidas). El punto es que las funciones virtuales existen para permitir la personalización; a menos que también deban invocarse directamente desde el código de las clases derivadas, no hay necesidad de hacer nada más que privado

+0

Como puede adivinar por mi respuesta, creo que la directriz n. ° 3 de Sutter más bien empuja a la directriz n. ° 2 por la ventana. – Spencer

54

Si el método es virtual, puede anularse por clases derivadas, incluso si es privado. Cuando se llama al método virtual, se invoca la versión anulada.

(opuesto a Herb Sutter citado por Prasoon Saurav en su respuesta, el C++ FAQ Lite recommends against private virtuals, sobre todo porque a menudo confunde a la gente.)

+32

Parece que C++ FAQ Lite ha cambiado su recomendación: "_la C++ FAQ anteriormente recomendaba el uso de virtuales protegidos en lugar de virtuales privadas. Sin embargo, el enfoque virtual privado ahora es lo suficientemente común como para confundir a los novatos._ " –

+2

Confusión de expertos, sin embargo, sigue siendo una preocupación. Ninguno de los cuatro profesionales de C++ que se sientan a mi lado estaban al tanto de los virtuales privados. – Newtonx

2

Las uso para permitir que las clases derivadas para "llenar los espacios en blanco" para una clase base sin exponer tal agujero a los usuarios finales. Por ejemplo, tengo objetos con mucho estado que se derivan de una base común, que solo puede implementar 2/3 de la máquina de estado general (las clases derivadas proporcionan el 1/3 restante según un argumento de plantilla, y la base no puede ser una plantilla para otras razones).

NECESITO tener la clase base común para hacer que muchas de las API públicas funcionen correctamente (estoy usando plantillas variadic), pero no puedo dejar ese objeto en la naturaleza. Peor aún, si dejo los cráteres en la máquina de estado -en forma de funciones virtuales puras- en cualquier lugar menos en "Privado", permito que un usuario inteligente o despistado derivado de una de sus clases secundarias anule los métodos que los usuarios nunca deberían tocar. Entonces, puse la máquina de estado 'cerebros' en funciones virtuales PRIVADAS. Luego, los hijos inmediatos de la clase base completan los espacios en blanco en sus anulaciones NO virtuales, y los usuarios pueden usar de forma segura los objetos resultantes o crear sus propias clases derivadas adicionales sin preocuparse por estropear la máquina de estado.

En cuanto al argumento de que no se deben TENER métodos virtuales públicos, digo BS. Los usuarios pueden anular indebidamente los virtuales privados con la misma facilidad que los públicos: después de todo, están definiendo clases nuevas. Si el público no debe modificar una API dada, no la haga virtual EN TODO en objetos de acceso público.

4

Me encontré por primera vez con este concepto al leer 'Effective C++' de Scott Meyers, Ítem 35: Considere alternativas a las funciones virtuales. Quería hacer referencia a Scott Mayers para otros que puedan estar interesados.

Es parte del patrón de método de plantilla a través de la interfaz no virtual idioma: los métodos públicos no son virtuales; más bien, envuelven las llamadas al método virtual que son privadas. La clase base puede entonces ejecutar la lógica antes y después de la llamada de función virtual privada:

public: 
    void NonVirtualCalc(...) 
    { 
    // Setup 
    PrivateVirtualCalcCall(...); 
    // Clean up 
    } 

creo que esto es un patrón de diseño muy interesante y estoy seguro que se puede ver cómo el control agregado es útil.

  • ¿Por qué hacer que la función virtual private? La mejor razón es que ya hemos proporcionado un método de orientación public.
  • ¿Por qué no hacerlo simplemente protected para que pueda usar el método para otras cosas interesantes? Supongo que siempre dependerá de su diseño y de cómo cree que se ajusta la clase base. Yo argumentaría que el creador de la clase derivada debería enfocarse en implementar la lógica requerida; todo lo demás ya está resuelto. Además, está la cuestión de la encapsulación.

Desde una perspectiva C++, es completamente legítimo anular un método virtual privado aunque no podrá llamarlo desde su clase. Esto es compatible con el diseño descrito anteriormente.

7

A pesar de todas las llamadas para declarar privado a un miembro virtual, el argumento simplemente no se sostiene. Con frecuencia, la anulación de una clase derivada de una función virtual tendrá que llamar a la versión de clase base. Puede no si es declarado private:

class Base 
{ 
private: 

int m_data; 

virtual void cleanup() { /*do something*/ } 

protected: 
Base(int idata): m_data (idata) {} 

public: 

int data() const { return m_data; } 
void set_data (int ndata) { m_data = ndata; cleanup(); } 
}; 

class Derived: public Base 
{ 
private: 
void cleanup() override 
{ 
    // do other stuff 
    Base::cleanup(); // nope, can't do it 
} 
public: 
Derived (int idata): base(idata) {} 
}; 

Usted tiene a declarar el método de la clase base protected.

Luego, tiene que tomar el feo recurso de indicar mediante un comentario que el método debe ser anulado pero no llamado.

class Base 
{ 
... 
protected: 
// chained virtual function! 
// call in your derived version but nowhere else. 
// Use set_data instead 
virtual void cleanup() { /* do something */ } 
... 

lo tanto de Herb Sutter directriz # 3 ... Pero el caballo está fuera del establo de todos modos.

Cuando se declara algo protected que está implícitamente confiar en el escritor de cualquier clase derivada de entender y utilizar correctamente las partes internas protegidas, sólo la forma de una declaración friend implica una confianza más profunda para private miembros.

Los usuarios que obtienen un mal comportamiento por violar esa confianza (por ejemplo, etiquetados como "despistado" al no molestarse en leer su documentación) solo tienen ellos mismos la culpa.

Actualización: He recibido algunos comentarios que afirman que puede "encadenar" implementaciones de funciones virtuales de esta manera mediante el uso de funciones virtuales privadas. Si es así, me gustaría verlo.

Los compiladores de C++ que uso definitivamente no permitirán que una implementación de clase derivada llame a una implementación de clase base privada.

Si el comité de C++ se relajó "en privado" para permitir este acceso específico, estaría todo para funciones virtuales privadas. Tal como están las cosas, todavía se nos aconseja cerrar la puerta del establo después de que el caballo es robado.

+0

No encuentro válido su argumento. Como desarrollador de una API debería esforzarse por una interfaz que sea ** difícil ** de usar incorrectamente y no establecer otro desarrollador para sus propios errores al hacerlo. Lo que desea hacer en su ejemplo podría implementarse con solo métodos virtuales privados. – sigy

+0

@sigy Entonces solo tendrá que publicar un respuesta que muestra cómo una función de clase derivada puede llamar a una función de clase base privada. – Spencer

+0

Eso no es lo que estaba diciendo. Pero puede reestructurar su código para lograr el mismo efecto sin la necesidad de llamar a una función de clase base privada – sigy

Cuestiones relacionadas