Leí una respuesta hace algún tiempo a una pregunta sobre dynamic_cast. Dynamic_cast no funcionó porque la clase base no tenía métodos virtuales. Una de las respuestas dice que derivar de clases sin métodos virtuales generalmente significa un mal diseño. ¿Es esto correcto? Incluso sin tomar ventaja del polimorfismo, todavía no puedo ver la incorrección al hacer esto.¿La herencia de una clase base sin métodos virtuales es mala práctica?
Respuesta
que depende de lo que estamos hablando:
- para las clases de rasgos (no hay datos) que está bien (
std::unary_function
viene a la mente) - para
private
herencia (utilizado en lugar de la composición de beneficiarse de Base vacía Optimización) está bien también
El problema surge cuando comienza a tratar políticamente este objeto Derivado en esta clase Base. Si alguna vez logras esa posición, entonces es el código de olor.
Nota: Incluso cuando se indica como bien arriba, todavía está proporcionando la capacidad de utilizar la clase polimórficamente, y por lo tanto se expone a errores sutiles.
La herencia sin métodos virtuales en C++ no es más que una reutilización de código. No puedo pensar en la herencia sin polimorfismo.
Derivar de una clase es siempre una opción válida, en aras de la reutilización de código.
A veces, no estamos buscando comportamiento polimórfico. Está bien, hay una razón por la que tenemos esa opción. Sin embargo, si este es el caso, considere usar herencia privada; si su clase no es polimórfica, no hay razón para que alguien intente usarla de forma polimórfica.
No usaría * inheritance * just * para la reutilización de código. Complácenos mucho * mejor * para la tarea, y el uso de Herencia le ata mucho más a la clase base y lo expone a errores sutiles (conversión/ocultación). –
@ Matthieu: un argumento válido, aunque todavía discutible. Es cierto que definitivamente requiere atención, así como la responsabilidad del usuario final. Estoy de acuerdo en que la composición es * generalmente * mejor, pero la herencia privada sigue siendo una alternativa (aunque, más difícil de usar correctamente). –
He encontrado la herencia privada viable en dos casos: como una optimización (EBO) o para anular los métodos virtuales (no se refiere aquí). Todos los demás son una cuestión de pereza/conveniencia. ¿Mencioné que estaba muy tenso con el debate de Herencia/Composición? –
Aquí es un ejemplo bien, a los comportamientos de los factores en las políticas (nótese el destructor protegido):
struct some_policy
{
// Some non-virtual interface here
protected:
~some_policy() { ... }
private:
// Some state here
};
struct some_class : some_policy, some_other_policy { ... };
Otro ejemplo Ok, para evitar excesos en el código de las plantillas. Tenga en cuenta el destructor protegido:
struct base_vector
{
// Put everything which doesn't depend
// on a template parameter here
protected:
~base_vector() { ... }
};
template <typename T>
struct vector : base_vector
{ ... };
Otro ejemplo, llamado CRTP. Tenga en cuenta el destructor protegida:
template <typename Base>
struct some_concept
{
void do_something { static_cast<Base*>(this)->do_some_other_thing(); }
protected:
~some_concept() { ... }
};
struct some_class : some_concept<some_class> { ... };
Otro ejemplo, llamado vacío Optimización de Base. No es realmente herencia en sí misma, ya que es más un truco permitir que el compilador no reserve espacio en some_class
para la clase base (que actúa como un miembro privado).
template <typename T>
struct some_state_which_can_be_empty { ... };
template <typename T>
struct some_class : private some_state_which_can_be_empty<T> { ... };
Como regla general, las clases heredadas deben tener destructor virtual o protegido.
Encuentro la herencia 'pública' en relación con. –
@Alexandre C: tanto las políticas como el ejemplo de bloat de código podrían usar composición en su lugar (siempre que no se requiera EBO). El ejemplo de CRTP es realmente especial, ya que no permite el polimorfismo en el sentido tradicional (la clase base depende de la clase derivada). –
@Matthieu: el código inflado y el ejemplo de política suelen tener alguna interfaz pública no trivial. Si los haces miembros, entonces tienes que escribir los contenedores a mano. Engorroso en código genérico y C++ 03 (más fácil con C++ 0x y reenvío perfecto). –
Algunas clases en la biblioteca estándar de C++ tienen miembros protegidos (que solo son significativos para una clase derivada) pero no funciones de miembros virtuales. Es decir, están diseñados para derivación, sin tener virtuales. Esto demuestra que generalmente debe ser un mal diseño para derivar de una clase sin virtuales.
Cheers & hth.
- 1. ¿Es una mala práctica tener estado en una clase estática?
- 2. es @unlink una mala práctica?
- 3. ¿Es una mala práctica para una clase tener solo campos y métodos estáticos?
- 4. Sobrecarga de la herencia de C++ sin funciones virtuales
- 5. Interfaz sin miembros: ¿mala práctica?
- 6. Usando métodos setter en constructor: ¿mala práctica?
- 7. ¿Está utilizando una instancia de una clase para acceder a métodos estáticos considerados como mala práctica?
- 8. ¿La mala práctica de "arrojar excepciones" es?
- 9. Herencia múltiple: tamaño de la clase para punteros virtuales?
- 10. ¿Las funciones virtuales no puras con los parámetros son una mala práctica?
- 11. Clonación clase C++ con métodos virtuales puros
- 12. ¿Los métodos que implementan métodos virtuales puros de una clase de interfaz deben declararse también virtuales?
- 13. ¿Es una mala práctica escribir a $ _POST?
- 14. ¿Es una mala práctica usar getattr de python extensivamente?
- 15. ¿Cuándo es apropiado usar métodos virtuales?
- 16. ¿Es una mala práctica usar el módulo requireJS como singleton?
- 17. ¿La palabra clave "con" Delphi es una mala práctica?
- 18. ¿El uso de procedimientos almacenados es una mala práctica?
- 19. conexión de base de datos de php singleton, ¿es este código una mala práctica?
- 20. Java - Métodos virtuales
- 21. ¿Es una buena o mala práctica llamar a métodos de instancia desde un constructor java?
- 22. métodos no virtuales Sustitución
- 23. Creando una clase de AppToolbox de Catch-All: ¿es esta una mala práctica?
- 24. Herencia de Python: ¿se invocan métodos de clase base dentro de la clase secundaria?
- 25. ¿Los argumentos NULL son una mala práctica?
- 26. métodos virtuales puros C++
- 27. ¿Es una mala práctica usar Reflection in Unit testing?
- 28. C# métodos estáticos virtuales (o abstractos)
- 29. ¿Es una mala práctica generar aleatoriamente datos de prueba?
- 30. ¿Es una mala práctica agregar propiedades a los nodos DOM?
Es bueno señalar que es posible el uso de polimorfos; sin embargo, si su clase está definida como se describe, el usuario tiene la responsabilidad de no hacer un esfuerzo adicional para eludir esto. –
@Ken: El problema es sobre esta responsabilidad. Incluso si no es intencionado, un usuario * puede * estropearse y usar la clase polimórficamente. De acuerdo, en ambos casos es poco probable. En el primero porque un rasgo no tiene métodos no estáticos, es inútil y en este último "privado" restringe la posibilidad de error a los métodos de la clase y amigos. Aún así, puede suceder. El problema (en general) es una característica que falta: quisiéramos * delegación * aquí, no * herencia *. –