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()
Compare
f1()
yf6()
. Sabemos queoverride
yfinal
es indepenticamente setemáticamente.-
override
significa que la funciónoverride
una función virtual en su clase base. Verf1()
yf3()
. -
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.) Verf6()
yf4()
.
-
Compare
f2()
yf3()
. Sabemos que si una función miembro se declara sinvirtual
y confinal
, significa que ya anula una función virtual en la clase base. En este caso, laoverride
palabra clave es redundante.Compare
f4()
yf5()
. Sabemos que si una función miembro se declara convirtual
y si no es la primera función virtual en la jerarquía de herencia, entonces debemos usar laoverride
para especificar la relación de anulación. De lo contrario, podemos agregar accidentalmente nueva función virtual en la clase derivada.Compare
f1()
yf7()
. Sabemos que cualquier función miembro, no solo las virtuales, puede anularse en la clase derivada. Lo que especificavirtual
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).Compare
f7()
yf8()
. Sabemos que podemos incluso anular una función de clase base y convertirla en una nueva virtual. (Lo que significa que cualquier función miembrof8()
de la clase derivada deD
será virtual.) (Esto también debe evitarse en la práctica).Compare
f7()
yf9()
. Sabemos que laoverride
puede ayudarnos a encontrar el error cuando queremos anular una función virtual en la clase derivada, mientras que olvidó agregar la palabra clavevirtual
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 lafinal
.
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 claseB
está marcada con el especificador de virtudesfinal
y en una claseD
derivada deB
una funciónD::f
prevalece sobreB::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.