2009-07-28 12 views
8

¿Por qué se recomienda no tener miembros de datos en la clase base virtual?Miembros de datos de la clase base virtual

¿Qué pasa con los miembros de la función? Si tengo una tarea común a todas las clases derivadas, ¿está bien que la clase base virtual realice la tarea o la heredada derivada de dos clasificadas, desde la interfaz virtual y la base simple que hacen la tarea?

Gracias.

+6

¿Podría proporcionar un enlace o una cita para el consejo? No puedo ver mucho si marca una clase base virtual si no tiene miembros de datos, ya que el propósito más común de la herencia virtual es evitar la duplicación de miembros de la clase base. Por ejemplo, en las bibliotecas estándar, ios_base tiene miembros de datos, y es una clase base virtual (vía ios) de istream y ostream. Así que casi (pero no del todo) diría lo contrario: si vas a tener una clase base virtual, entonces debería tener miembros de datos, o bien heredar de forma no virtual. –

Respuesta

11

Como práctica sólo se debe utilizar la herencia virtual para definir interfaces ya que se utilizan por lo general con la herencia múltiple para asegurarse de que sólo una versión de la clase está presente en la clase derivada. Y las interfaces puras son la forma más segura de herencia múltiple. Por supuesto, si sabe lo que está haciendo, puede usar la herencia múltiple como quiera, pero puede dar como resultado un código quebradizo si no tiene cuidado.

La mayor desventaja con la herencia virtual es si sus constructores toman los parámetros. Si tiene que pasar parámetros al constructor de una clase base virtual, fuerza a todas las clases derivadas a llamar explícitamente al constructor (no pueden confiar en una clase base que llame al constructor).

La única razón que puedo ver para su consejo explícito es que los datos en su clase base virtual pueden requerir parámetros de constructor.

Editar Hice algunos trabajos a domicilio después del comentario de Martin, gracias Marin. La primera línea no es del todo cierto:

Como práctica sólo se debe utilizar herencia virtual para definir las interfaces ya que suelen ser utilizados con la herencia múltiple para asegurar que sólo una versión de la clase es presente en la clase derivada.

La herencia virtual no hace ninguna diferencia si la clase base es una interfaz pura (excepto para errores de compilador ligeramente diferentes, en vc8, si no se implementan todos los métodos). Sólo se hace una diferencia real si la clase base tiene datos, en este caso se termina con un diamante en lugar de una forma de U

Non virtual virtual 
    A  A   A 
    |  |  / \ 
    B  C  B  C 
    \ /  \ /
    D    D 

En el caso virtuales B y C comparten la misma copia de A.

Sin embargo, todavía estoy de acuerdo con todo lo demás sobre interfaces puras que son la forma más segura de herencia múltiple, incluso si no requieren herencia virtual. Y el hecho de que los parámetros del constructor y la herencia virtual son un problema.

+0

+1 Esto también se resume en las preguntas frecuentes de C++ Lite http://www.new-brunswick.net/workshop/c++/faq/multiple-inheritance.html#faq-25.8 –

+0

No creo que nada de eso sea cierto. –

+0

@Martin: por desgracia, es ... –

0

a) los miembros deben ser privados - por lo que puede tener problemas de utilizarlos en las clases derivadas (por lo que hay que añadir los métodos get y set, que soplan su interfaz)

b) no declaro cosas Actualmente no utilizan - declarar variables sólo en clases que el acceso/usarlos

c) las clases base virtuales sólo deben contener las interfaces/métodos virtuales, nada más

Espero que ayude un poco, incluso si mis razones no son perfectas y completas :)

ciao, Chris

1

Nunca he visto esta recomendación.

Una clase es un conjunto de funciones y datos estrechamente relacionados. El objetivo de una clase base es tener un conjunto común de funciones y datos que estén disponibles para su reutilización por clases derivadas.

Creo que la restricción de no tener miembros de datos o funciones virtuales no puras en clases base reducirá la cantidad de reutilización de código, lo que conduce a un código menos confiable en el largo plazo.

+1

Si puede obtener una copia de "C++ efectivo", lea el elemento 20: "Evitar miembros de datos en interfaces públicas". – quark

+0

Correcto, estoy de acuerdo con esto. No estoy sugiriendo que los miembros de los datos sean públicos. En cualquier caso, malentendí la pregunta y creo que la respuesta de iain es buena. –

2

El consejo básico es tener un constructor predeterminado en la base virtual. Si no lo hace, entonces cada clase más derivada (es decir. Cualquier subclase) debe llamar a la ctor base virtual de forma explícita, y que conduce a los colegas de ira llamando a la puerta de su oficina ...

class VirtualBase { 
public: 
    explicit VirtualBase(int i) : m_i(i) {} 
    virtual ~VirtualBase() {} 

private: 
    int m_i; 
}; 

class Derived : public virtual VirtualBase { 
public: 
    Derived() : VirtualBase(0) {} // ok, this is to be expected 
}; 

class DerivedDerived : public Derived { // no VirtualBase visible 
public: 
    DerivedDerived() : Derived() {} // ok? no: error: need to explicitly 
            // call VirtualBase::VirtualBase!! 
    DerivedDerived() : VirtualBase(0), Derived() {} // ok 
}; 
+0

¿Quiere decir que si no llamé a la base virtual ctor explícitamente, no se invocará cuando creo el objeto derivado? – jackhab

+0

Tiene que llamar al ctor de la clase base virtual en la implementación de cada ctor derivado, el compilador no le permitirá salirse con la suya de lo contrario. La única excepción: si la base virtual tiene un ctor predeterminado, se llama a ese cuando no se llama explícitamente a otro. –

0

Todos -abtract clases base que se utilizan para simular interfaces en C++ no deberían tener miembros de datos, porque están describiendo una interfaz , y el estado de la instancia es un detalle de implementación.

Aparte de eso, las clases base que contienen funciones virtuales pueden tener miembros de datos. Las reglas habituales se aplican, sin embargo:

  • hágales tan ocultos como sea posible, y use getters y setters si necesita conservar invariantes de clase.
    (Hay una laguna en aquí: si no hay invariante asociada con el miembro, es decir, puede asumir cualquier valor posible en un momento dado, es posible hacerlo público Sin embargo, esto niega una clase derivada de añadir un invariante
  • .. Diseño para la herencia: el contrato de su clase debe definir las responsabilidades y posibilidades de una clase derivada, lo que necesita/puede sobreescribir con qué propósito.
Cuestiones relacionadas