¿TODAS las funciones virtuales deben implementarse en clases derivadas?

91

Esta puede parecer una pregunta simple, pero no puedo encontrar la respuesta en ningún otro lugar.

Supongamos que tengo lo siguiente:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

¿Está bien que la clase Derived no implemente la función bar ()? ¿Qué pasa si no TODAS mis clases derivadas necesitan la función bar (), pero algunas sí? ¿Todas las funciones virtuales de una clase base abstracta deben implementarse en las clases derivadas, o solo en las que son puramente virtuales? Gracias

mikestaub
fuente

Respuestas:

82

Las clases derivadas no tienen que implementar todas las funciones virtuales por sí mismas. Solo necesitan implementar los puros . 1 Eso significa que la Derivedclase de la pregunta es correcta. Se hereda la baraplicación de su clase ancestro, Abstract. (Esto supone que Abstract::barestá implementado en alguna parte. El código de la pregunta declara el método, pero no lo define. Puede definirlo en línea como muestra la respuesta de Trenki , o puede definirlo por separado).


1 E incluso entonces, solo si se va a crear una instancia de la clase derivada . Si una clase derivada no se instancia directamente, sino que solo existe como una clase base de más clases derivadas, entonces son esas clases las que son responsables de implementar todos sus métodos virtuales puros. A la clase "media" en la jerarquía se le permite dejar algunos métodos virtuales puros sin implementar, al igual que la clase base. Si la clase "media" hace poner en práctica un método virtual pura, después sus descendientes heredarán que la aplicación, por lo que no tienen que volver a poner en práctica ellos mismos.

Rob Kennedy
fuente
3
E incluso esto (implementación de funciones virtuales puras) solo si están destinadas a ser instanciadas (en contraste con ser una clase base abstracta en sí mismas).
Christian Rau
1
Eso es lo que pense. Pero estoy haciendo esto en mi proyecto, y obtengo un error de vinculación que dice que hay un "símbolo externo sin resolver" para Derived :: bar (); Pero nunca declaré bar dentro de Derived, entonces, ¿por qué el enlazador busca el cuerpo de la función?
mikestaub
1
@pixelpusher Por supuesto, Derived::bartiene un cuerpo funcional, el de Abstract::bar. Entonces, parece que la unidad de traducción donde se define (¿está definida en algún lugar?) No está vinculada a la unidad de traducción donde se llama.
Christian Rau
2
@Rob: They only need to implement the pure ones.Es engañoso. Las clases derivadas tampoco necesitan implementar funciones virtuales puras .
Nawaz
Agradezco la ayuda, pero @trenki dio en el clavo. Aunque también estabas en lo cierto Christian Rau, ya que NO estaba definido.
mikestaub
47

Solo los métodos virtuales puros deben implementarse en clases derivadas, pero aún necesita una definición (y no solo una declaración) de los otros métodos virtuales. Si no proporciona uno, es muy posible que el enlazador se queje.

Entonces, simplemente poner {}después de su método virtual opcional le da una implementación predeterminada vacía:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Sin embargo, una implementación predeterminada más complicada iría en un archivo fuente separado.

trenki
fuente
7

El estándar ISO C ++ especifica que se deben definir todos los métodos virtuales de una clase que no sean puramente virtuales.

En pocas palabras, la regla es:
si su clase derivada anula el método virtual de la clase Base, entonces también debería proporcionar una definición.

Según la regla anterior en su ejemplo de código, virtual void bar();necesita una definición en la clase Base.

Referencia:

Estándar C ++ 03: 10.3 Funciones virtuales [class.virtual]

Una función virtual declarada en una clase deberá definirse o declararse pura (10.4) en esa clase, o ambas; pero no se requiere diagnóstico (3.2).

Por lo tanto, debe hacer que la función sea puramente virtual o proporcionar una definición para ella.

El faq de gcc también lo documenta:

El estándar ISO C ++ especifica que todos los métodos virtuales de una clase que no son puramente virtuales deben definirse, pero no requiere ningún diagnóstico de violaciones de esta regla [class.virtual]/8. Con base en esta suposición, GCC solo emitirá los constructores definidos implícitamente, el operador de asignación, el destructor y la tabla virtual de una clase en la unidad de traducción que define su primer método no en línea.

Por lo tanto, si no logra definir este método en particular, el enlazador puede quejarse de la falta de definiciones para símbolos aparentemente no relacionados. Desafortunadamente, para mejorar este mensaje de error, podría ser necesario cambiar el vinculador, y esto no siempre se puede hacer.

La solución es asegurar que todos los métodos virtuales que no sean puros estén definidos. Tenga en cuenta que se debe definir un destructor incluso si se declara virtual puro [class.dtor]/7.

Alok Save
fuente
3

Sí, está bien ... solo necesita implementar cualquier función virtual pura para crear una instancia de una clase derivada de una clase base abstracta.

Jason
fuente
1

Sí, es correcto que una clase derivada tenga que ANULAR la función que es pura virtual en la clase principal. La clase principal que tiene una función virtual pura se llama clase abstracta solo porque su clase secundaria debe proporcionar su propio cuerpo de la función virtual pura.

Para las funciones virtuales normales: - No es necesario anularlas más, ya que algunas clases secundarias pueden tener esa función, otras no.

El propósito principal del mecanismo de función virtual es el polimorfismo en tiempo de ejecución, ya sea que el propósito principal de la función virtual pura (clase abstracta) sea hacer que sea obligatorio tener el mismo nombre de función con el propio cuerpo.

CodeCodeCode
fuente