statement method español c++ override final virtual-functions

method - override statement c++



¿El final implica anular? (5)

(Pase al final para ver la conclusión si tiene prisa).

Tanto la override como la final pueden aparecer solo en declaración en una función virtual. Y ambas palabras clave se pueden usar en la misma declaración de función, pero si es útil usarlas, ambas dependen de las situaciones.

Tome el siguiente código como ejemplo:

#include <iostream> using std::cout; using std::endl; struct B { virtual void f1() { cout << "B::f1() "; } virtual void f2() { cout << "B::f2() "; } virtual void f3() { cout << "B::f3() "; } virtual void f6() final { cout << "B::f6() "; } void f7() { cout << "B::f7() "; } void f8() { cout << "B::f8() "; } void f9() { cout << "B::f9() "; } }; struct D : B { void f1() override { cout << "D::f1() "; } void f2() final { cout << "D::f2() "; } void f3() override final { cout << "D::f3() "; } // need not have override // should have override, otherwise add new virtual function virtual void f4() final { cout << "D::f4() "; } //virtual void f5() override final; // Error, no virtual function in base class //void f6(); // Error, override a final virtual function void f7() { cout << "D::f7() "; } virtual void f8() { cout << "D::f8() "; } //void f9() override; // Error, override a nonvirtual function }; int main() { B b; D d; B *bp = &b, *bd = &d; D *dp = &d; bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl; bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl; dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl; return 0; }

El resultado es

B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9() D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9() D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()

  1. Compare f1() y f6() . Sabemos que override y final es indepenticamente setemáticamente.

    • override significa que la función override una función virtual en su clase base. Ver f1() y f3() .
    • final significa que la función no puede ser anulada por su clase derivada. (Pero la función en sí no necesita anular una función virtual de la clase base.) Ver f6() y f4() .
  2. Compare f2() y f3() . Sabemos que si una función miembro se declara sin virtual y con final , significa que ya anula una función virtual en la clase base. En este caso, la override palabra clave es redundante.

  3. Compare f4() y f5() . Sabemos que si una función miembro se declara con virtual y si no es la primera función virtual en la jerarquía de herencia, entonces debemos usar la override para especificar la relación de anulación. De lo contrario, podemos agregar accidentalmente nueva función virtual en la clase derivada.

  4. Compare f1() y f7() . Sabemos que cualquier función miembro, no solo las virtuales, puede anularse en la clase derivada. Lo que especifica virtual es polimorfismo , lo que significa que la decisión sobre qué función ejecutar se retrasa hasta el tiempo de ejecución en lugar del tiempo de compilación. (Esto debería evitarse en la práctica).

  5. Compare f7() y f8() . Sabemos que podemos incluso anular una función de clase base y convertirla en una nueva virtual. (Lo que significa que cualquier función miembro f8() de la clase derivada de D será virtual.) (Esto también debe evitarse en la práctica).

  6. Compare f7() y f9() . Sabemos que la override puede ayudarnos a encontrar el error cuando queremos anular una función virtual en la clase derivada, mientras que olvidó agregar la palabra clave virtual en la clase base.

En conclusión , la mejor práctica en mi opinión es:

  • solo use virtual en la declaración de la primera función virtual en la clase base;
  • siempre use la override para especificar la anulación de la función virtual en la clase derivada, a menos que también se especifique la final .

Según tengo entendido, la palabra clave override establece que una declaración determinada implementa un método virtual base, y la compilación debería fallar si no se encuentra un método base coincidente.

Mi comprensión de la palabra clave final es que le dice al compilador que ninguna clase anulará esta función virtual .

Entonces, ¿es la override final redundante? Parece compilar bien . ¿Qué información override final transmitir que final no? ¿Cuál es el caso de uso para tal combinación?


El siguiente código (con el especificador final ) se compila. Pero la compilación falla cuando se reemplaza la override final con la override final . Por lo tanto, la override final transmite más información (y evita la compilación) que solo la final .

class Base { public: virtual ~Base() {} }; class Derived : public Base { public: virtual void foo() final { std::cout << "in Derived foo/n"; } };

Básicamente, la override final dice que este método no se puede anular en ninguna clase derivada y este método anula un método virtual en una clase base. final solo no especifica la parte de anulación de la clase base.


No final no implica necesariamente override . De hecho, puede declarar que una función virtual que declara inmediatamente final ve aquí . La palabra clave final simplemente indica que ninguna class derivada puede crear una anulación de esta función.

La palabra clave override es importante porque impone que en realidad está anulando una función virtual (en lugar de declarar una nueva no relacionada). Ver esta publicación con respecto a override

En resumen, cada uno sirve su propio propósito particular, y a menudo es correcto usar ambos.


final no implica necesariamente que la función esté anulada. Es perfectamente válido (si tiene un valor algo dudoso) declarar una función virtual como final en su primera declaración en la jerarquía de herencia.

Una razón por la que puedo pensar para crear una función virtual e inmediatamente final es si desea evitar que una clase derivada le dé al mismo nombre y parámetros un significado diferente.


final no requiere que la función anule nada en primer lugar. Su efecto se define en [class.virtual] / 4 como

Si una función virtual f en alguna clase B está marcada con el especificador de virtudes final y en una clase D derivada de B una función D::f prevalece sobre B::f , el programa está mal formado.

Eso es. Ahora override final simplemente significaría
"Esta función anula una clase base uno ( override ) y no puede ser anulada por sí misma ( final )".
final por sí mismo impondría un requisito más débil. override y final tienen comportamiento independiente.

Sin embargo, tenga en cuenta que el final solo se puede usar para funciones virtuales - [class.mem] / 8

Un virt-specifier-seq debe aparecer solo en la declaración de una función de miembro virtual (10.3).

De ahí la declaración

void foo() final;

Es efectivamente lo mismo que

virtual void foo() final override;

Dado que ambos requieren que foo anule algo, la segunda declaración al usar la override , y la primera siendo válida si y solo si foo es implícitamente virtual, es decir, cuando foo anula una función virtual llamada foo en una clase base, lo que hace foo en el derivado automáticamente virtual. Por lo tanto, la override sería superflua en las declaraciones donde ocurre la final , pero no virtual .
Aún así, la última declaración expresa la intención mucho más clara y definitivamente debería ser preferida.