¿Final implica reemplazar?


Según entiendo, la palabra clave override indica que una declaración dada implementa un método base virtual, y la compilación debería fallar si no se encuentra ningún método base coincidente.

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

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

Author: Columbo, 2015-04-02

5 answers

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

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

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


Tenga en cuenta que final solo se puede usar para funciones virtuales, aunque - [clase.mem]/8

A virt-specifier-seq figurará únicamente en la declaración de A función 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 foo para anular algo-la segunda declaración usando override, y la primera siendo válida si y solo si foo es implícitamente virtual, es decir, cuando foo está sobreescribiendo una función virtual llamada foo en una clase base, que hace que foo en la derivada sea automáticamente virtual. Así, override sería superfluo en declaraciones donde final, pero no virtual, ocurre.
Sin embargo, esta última declaración expresa la intención mucho más claramente y sin duda debería preferirse.

 34
Author: Columbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-03 12:22:50

final no implica necesariamente que la función se anula. 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 que se me ocurre 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.

 13
Author: Angew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-02 12:04:10

(Salta al final para ver la conclusión si tienes prisa.)

Tanto override como final pueden aparecer solo en la 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 depende 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;
}

La salida 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 independiente semáticamente.

    • override significa que la función está sobreescribiendo una función virtual en su clase base. Véanse 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 clase base.) Véanse 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 sobrescribe una función virtual en clase baja. En este caso, la palabra clave override es redundante.

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

  4. Compare f1() y f7(). Sabemos que cualquier función miembro, no solo los virtuales, puede ser anulado en clase derivada. Lo que virtual especifica 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 debe evitarse en la práctica.)

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

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

En conclusión , la mejor práctica en mi opinión es: {[38]]}

  • solo use virtual en la declaración de la primera función virtual en la clase base;
  • utilice siempre override para especificar la función virtual de anulación en clase derivada, a menos que también se especifique final.
 4
Author: Jaege,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-25 12:04:34

No final no implica necesariamente override. De hecho, podrías declarar una función virtual que declaras inmediatamente final ver aquí . La palabra clave final simplemente indica que ningún class derivado puede crear una anulación de esta función.

La palabra clave override es importante en el sentido de que obliga a que en realidad se está sobrescribiendo una función virtual (en lugar de declarar una nueva no relacionada). Ver este post con respecto a override

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

 1
Author: CoryKramer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:10

El siguiente código (con el especificador final) compila. Pero la compilación falla cuando final se reemplaza por override final. Así, override final transmite más información (e impide la compilación) que solo final.

class Base
{
public:
    virtual ~Base() {}
};

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

Esencialmente, override finaldice que este método no puede ser anulado en ninguna clase derivada y este método anula un método virtual en una clase base. final por sí solo no especifica la parte de sobreescritura de la clase base.

 1
Author: tcb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-06 08:28:29