que - ¿Por qué realmente necesitamos herencia Privada o Protegida en C++?
polimorfismo c++ (7)
En C ++, no puedo pensar en un caso en el que me gustaría heredar privado / protegido de una clase base:
class Base;
class Derived1 : private Base;
class Derived2 : protected Base;
¿Es realmente útil?
Es útil cuando desea tener acceso a algunos miembros de la clase base, pero sin exponerlos en su interfaz de clase. La herencia privada también puede verse como un tipo de composición: el faq-lite de C ++ 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 puede escribir la clase de automóvil 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 forma de hacer tiene varias desventajas:
- tu intención es mucho menos clara
- puede conducir a una herencia múltiple abusiva
- rompe la encapsulación de la clase Engine ya que puedes acceder a sus miembros protegidos
- está permitido anular los métodos virtuales del motor, que es algo que no quieres si tu objetivo es una composición simple
He usado herencias privadas y protegidas en un momento u otro.
La herencia privada es útil cuando desea que algo tenga el comportamiento de la clase base, y luego puede anular esa funcionalidad, pero no desea 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 tienes una clase base que deriva funcionalidades útiles de otra clase, pero solo quieres que sus clases derivadas puedan usarla.
La herencia privada se usa 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 have-a no funciona debido al uso demasiado celoso de "protected", pero es mejor arreglar la clase rota que romper una nueva clase.
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 sobre el tema . Explica cuándo elegiría la herencia no pública sobre la contención para los detalles de implementación.
Por ejemplo, cuando quiere reutilizar la implementación, pero no la interfaz de una clase Y anula sus funciones virtuales.
Privado puede ser útil en bastantes circunstancias. Solo uno de ellos son políticas:
¿Es la especialización de plantilla de clase parcial la respuesta a este problema de diseño? .
Otra ocasión en la que es útil es prohibir copiar y asignar:
struct noncopyable {
private:
noncopyable(noncopyable const&);
noncopyable & operator=(noncopyable const&);
};
class my_noncopyable_type : noncopyable {
// ...
};
Como no queremos que el usuario tenga un puntero de tipo no noncopyable*
a nuestro objeto, derivamos 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).
Una vez implementé estas estructuras de datos como clases:
- Lista enlazada
- Arsenal genérico (abstracto)
- Arreglo simple (hereda de una matriz genérica)
- Gran matriz (hereda de una matriz genérica)
La interfaz del gran conjunto haría que pareciera una matriz, sin embargo, en realidad era una lista enlazada de arreglos simples de tamaño fijo. Así que lo declare así:
template <typename T>
class CBigArray : public IArray, private CLnkList {
// ...