2008-12-17 22 views
50

En C++, no se puede pensar en un caso en el que me gustaría heredar privado/protegido de una clase base de :¿Por qué necesitamos herencia Privada o Protegida en C++?

class Base; 
class Derived1 : private Base; 
class Derived2 : protected Base; 

¿Es realmente útil?

+4

Considere esto: un círculo es una elipse pero [el círculo no es sustituible para Ellipse] (http://stackoverflow.com/questions/7602102/teach-dynamic-polymorphism-with-simple-example/7677015#7677015), la herencia pública es *** no *** y es una relación, aunque a menudo lo llamamos así. – spraff

+5

El círculo es de hecho una elipse. ¿No estoy seguro de lo que quieres decir? –

Respuesta

41

Es útil cuando desea tener acceso a algunos miembros de la clase base, pero sin exponerlos en su interfaz de clase. herencia privada también puede ser visto como una especie de composición: la C++ faq-lite da el siguiente ejemplo para ilustrar esta afirmación

class Engine { 
public: 
    Engine(int numCylinders); 
    void start();     // Starts this Engine 
}; 

class Car { 
    public: 
    Car() : e_(8) { }    // Initializes this Car with 8 cylinders 
    void start() { e_.start(); } // Start this Car by starting its Engine 
    private: 
    Engine e_;     // Car has-a Engine 
}; 

Para obtener la misma semántica, también se podría escribir la categoría de coches de la siguiente manera:

class Car : private Engine { // Car has-a Engine 
public: 
    Car() : Engine(8) { }   // Initializes this Car with 8 cylinders 
    using Engine::start;   // Start this Car by starting its Engine 
}; 

Sin embargo, esta manera de hacer tiene varias desventajas:

  • su intención es mucho menos claro
  • que puede conducir a la herencia múltiple abusiva
  • se rompe la encapsulación de la clase del motor ya que se puede acceder a sus miembros protegidos
  • se le permite reemplazar los métodos virtuales del motor, que es algo que no desea si su objetivo es una composición simple
+18

En mi opinión, en el caso de "tiene un" no debería haber ninguna herencia, pero debería haber una composición. Creo que este es un mal ejemplo de abuso de herencia, ya que confunde a los usuarios. –

+7

El segundo ejemplo es un muy mal ejemplo, de otro modo terminaría así: clase Auto: motor privado, rueda privada, asiento privado – Lodle

+1

Sí "has-a" no debería ser herencia. Uso la herencia privada para "is-inserted-in-temrs-of". que en realidad significa "es-a", pero como una especie de truco. Al igual que reutilizaron un fragmento de código que en realidad no debía dar este tipo de descendencia, pero bueno ... –

38

Privado puede ser útil en bastantes circunstancias. Solo uno de ellos son políticas:

Is partial class template specialization the answer to this design problem?.

Otra ocasión en la que es útil es prohibir la copia y la asignación de:

struct noncopyable { 
    private: 
    noncopyable(noncopyable const&); 
    noncopyable & operator=(noncopyable const&); 
}; 

class my_noncopyable_type : noncopyable { 
    // ... 
}; 

Porque no queremos que el usuario tiene un puntero de tipo noncopyable* a nuestro objeto, que se derivan de forma privada. Eso cuenta no solo para las clases que no se pueden copiar, sino también para muchas otras clases (las políticas son las más comunes).

+1

Realmente no importa si se obtiene no copiable pública o privadamente, ya que el constructor de copias y el operador de asignación son privados de todos modos. – Marcin

+6

Como @litb indica en su respuesta, la derivación privada impide que el usuario haga referencia a una instancia de my_non_copyable_type con un puntero o una referencia a un elemento no copiable. –

+2

sí, y eso también evita que los usuarios eliminen a través de un puntero al que no se puede copiar. –

15

Modelos de herencia pública IS-A.
Modelos de herencia no públicos IS-IMPLEMENTED-IN-TERMS-OF.
Modelos de contención HAS-A, que es equivalente a IS-IMPLEMENTED-IN-TERMS-OF.

Sutter on the topic. Explica cuándo elegiría la herencia no pública sobre la contención para los detalles de implementación.

3

Por ejemplo, cuando desea reutilizar la implementación, pero no la interfaz de una clase Y anular sus funciones virtuales.

0

He usado herencias privadas y protegidas en algún momento.

La herencia privada es útil cuando quiere que algo tenga el comportamiento de la clase base, y luego puede anular esa funcionalidad, pero no quiere que el mundo entero lo conozca y lo use. Aún puede usar la interfaz de una clase derivada de forma privada haciendo que una función devuelva esa interfaz. También es útil cuando puede hacer que las cosas se registren para escuchar las devoluciones de llamadas, ya que pueden registrarse usando la interfaz privada.

La herencia protegida es especialmente útil cuando tiene una clase base que deriva funcionalidades útiles de otra clase, pero solo desea que sus clases derivadas puedan usarla.

-1

vez he implementado estas estructuras de datos como clases:

  • lista enlazada
  • gama genérica (resumen)
  • simple matriz (hereda de matriz genérica)
  • Arsenal grande (hereda de matriz genérica)

La interfaz del gran conjunto haría que pareciera una matriz, sin embargo, en realidad se trataba de un enlace l isto de arreglos simples de tamaño fijo. Así que lo declare así:

template <typename T> 
class CBigArray : public IArray, private CLnkList { 
    // ... 
+2

Este es un buen ejemplo de cómo no usar herencia privada. – Arkadiy

3

La herencia privada se utiliza principalmente por un motivo equivocado. La gente lo usa para IS-IMPLEMENTED-IN-TERMS-OF, como se indicó en una respuesta anterior, pero en mi experiencia siempre es más limpio mantener una copia en lugar de heredar de la clase. Otra respuesta anterior, la de CBigArray, proporciona un ejemplo perfecto de este antipatrón.

Me doy cuenta de que puede haber casos en los que has-a no funciona debido al uso excesivo de "protegido", pero es mejor arreglar la clase averiada que romper una nueva clase.

+0

"_keep a copy_" member? – curiousguy

+0

sí, mantener un miembro. – Arkadiy

+0

Esa es una opción viable en muchos casos. Pero si alguna vez terminas con un "temido diamante de la muerte", entonces el compilador no te ayudará (con la herencia virtual), tendrás que arreglar el problema tú mismo. – curiousguy