Método virtual privado en C ++

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

Lo he notado en un proyecto C ++ de código abierto:

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

Herb Sutter lo ha explicado muy bien aquí .

Directriz 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 más 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 necesiten ser invocados directamente desde el código de las clases derivadas, no hay necesidad de hacer nada más que confidencial

Si el método es virtual, puede ser anulado 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, C ++ FAQ Lite recomienda contra los virtuales privados , sobre todo porque a menudo confunde a las personas).

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. No puede si se declara 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) {} }; 

Debes declarar protected método de la clase base.

Luego, debe 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 */ } ... 

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

Cuando declaras algo protected , confías implícitamente en que el escritor de cualquier clase derivada comprenda y utilice correctamente las partes internas protegidas, del mismo modo que una statement de friend implica una confianza más profunda para private miembros private .

Los usuarios que obtienen un mal comportamiento al violar esa confianza (por ejemplo, etiquetados como “despistados” 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 utilizando funciones virtuales privadas. Si es así, me gustaría verlo.

Los comstackdores 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.

Primero me encontré con este concepto mientras leía “Effective C ++” de Scott Meyers, artículo 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 expresión idiomática Non-Virtual Interface : 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 ejecutar lógica antes y después de la llamada a la función virtual privada:

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

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

  • ¿Por qué hacer que la función virtual sea private ? La mejor razón es que ya hemos proporcionado un método public .
  • ¿Por qué no 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 de 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.

Los uso para permitir que las clases derivadas “llenen 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 debes 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.