Namespaces
Variants

final specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Virtual function
override specifier (C++11)
final specifier (C++11)
Special member functions
Templates
Miscellaneous

Especifica que una virtual function no puede ser sobreescrita en una clase derivada, o que una clase no puede ser derived from .

Contenidos

Sintaxis

Cuando se aplica a una función miembro, el identificador final aparece inmediatamente después del declarador en la sintaxis de una declaración de función miembro o una definición de función miembro dentro de la definición de una clase.

Cuando se aplica a una clase (incluyendo struct y union), el identificador final aparece al principio de la definición de la clase, inmediatamente después del nombre de la clase, y no puede aparecer en una declaración de clase.

declarador secuencia-de-especificadores-virtuales  (opcional) especificador-puro  (opcional) (1)
declarador secuencia-de-especificadores-virtuales  (opcional) cuerpo-de-función (2)
clase-clave atributo  (opcional) nombre-cabecera-clase especificador-virtual-clase  (opcional) cláusula-base  (opcional) (3) (hasta C++26)
clase-clave atributo  (opcional) nombre-cabecera-clase secuencia-de-especificadores-de-propiedad-clase  (opcional) cláusula-base  (opcional) (4) (desde C++26)
1) En una declaración de función miembro, final puede aparecer en virt-specifier-seq inmediatamente después del declarador, y antes del pure-specifier , si se utiliza.
2) En una definición de función miembro dentro de una definición de clase, final puede aparecer en virt-specifier-seq inmediatamente después del declarador y justo antes de function-body .
3) En una definición de clase, final puede aparecer como class-virt-specifier inmediatamente después del nombre de la clase, justo antes de los dos puntos que inician la base-clause , si se utiliza.
4) En una definición de clase, final puede aparecer en class-prop-specifier-seq , si se utiliza, pero solo una vez.

En los casos (1,2) , virt-specifier-seq , si se utiliza, es override o final , o final override o override final . En el caso (3) , el único valor permitido de class-virt-specifier , si se utiliza, es final . En el caso (4) , la class-prop-specifier-seq , si se utiliza, puede tener cualquier número de especificadores de propiedad de clase (desde C++26) , pero cada uno puede aparecer como máximo una vez.

Explicación

Cuando se utiliza en una declaración o definición de función virtual, final especificador asegura que la función es virtual y especifica que no puede ser sobrescrita por clases derivadas. El programa estará mal formado (se generará un error en tiempo de compilación) en caso contrario.

Cuando se utiliza en una definición de clase, final especifica que esta clase no puede aparecer en la lista-de-especificación-de-base de otra definición de clase (en otras palabras, no puede ser derivada). El programa está mal formado en caso contrario (se genera un error en tiempo de compilación). final también puede usarse con una union , en cuyo caso no tiene efecto (aparte de en el resultado de std::is_final ) (desde C++14) , ya que las unions no pueden ser derivadas.

final es un identificador con un significado especial cuando se utiliza en una declaración de función miembro o cabecera de clase. En otros contextos, no está reservado y puede utilizarse para nombrar objetos y funciones.

Nota

En una secuencia de los siguientes tokens:

  1. uno de class , struct y union
  2. un identificador posiblemente calificado
  3. final
  4. uno de : y {

el tercer token final en la secuencia siempre se considera como un especificador en lugar de un identificador.

struct A;
struct A final {}; // OK, definición de struct A,
                   // no inicialización por valor de variable final
struct X
{
    struct C { constexpr operator int() { return 5; } };
    struct B final : C{}; // OK, definición de clase anidada B,
                          // no declaración de miembro campo de bits final
};
// Uso anormal de final.
struct final final // OK, definición de una estructura llamada `final` de la cual
{                  // no se puede heredar
};
// struct final final {}; // Error: redefinición de `struct final`, NO una
                          // definición de una variable `final` usando un especificador
                          // de tipo elaborado `struct final` seguido por una
                          // inicialización de agregado
// struct override : final {}; // Error: no se puede derivar del tipo base final;
                               // `override` en el contexto dado es un nombre normal
void foo()
{
    [[maybe_unused]]
    final final; // OK, declaración de una variable llamada `final` de tipo
                 // `struct final` 
}
struct final final; // OK, declaración de una variable llamada `final` de tipo
                    // `struct final` usando un especificador de tipo elaborado
int main()
{
}

Palabras clave

final

Ejemplo

struct Base
{
    virtual void foo();
};
struct A : Base
{
    void foo() final; // Base::foo es sobrescrito y A::foo es la sobrescritura final
    void bar() final; // Error: bar no puede ser final ya que no es virtual
};
struct B final : A // struct B es final
{
    void foo() override; // Error: foo no puede ser sobrescrito ya que es final en A
};
struct C : B {}; // Error: B es final

Salida posible:

main.cpp:9:10: error: 'void A::bar()' marked 'final', but is not virtual
    9 |     void bar() final; // Error: bar cannot be final as it is non-virtual
      |          ^~~
main.cpp:14:10: error: virtual function 'virtual void B::foo()' overriding final function
   14 |     void foo() override; // Error: foo cannot be overridden as it is final in A
      |          ^~~
main.cpp:8:10: note: overridden function is 'virtual void A::foo()'
    8 |     void foo() final; // Base::foo is overridden and A::foo is the final override
      |          ^~~
main.cpp:17:8: error: cannot derive from 'final' base 'B' in derived type 'C'
   17 | struct C : B // Error: B is final
      |

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 11 Clases [class]
  • 11.7.3 Funciones virtuales [class.virtual]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 11 Clases [class]
  • 11.7.2 Funciones virtuales [class.virtual]
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 12 Clases [class]
  • 13.3 Funciones virtuales [class.virtual]
  • Estándar C++14 (ISO/IEC 14882:2014):
  • 9 Clases [class]
  • 10.3 Funciones virtuales [class.virtual]
  • Estándar C++11 (ISO/IEC 14882:2011):
  • 9 Clases [class]
  • 10.3 Funciones virtuales [class.virtual]

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 1318 C++11 una definición de clase que tiene final después del nombre de la clase y una
lista de especificación de miembros vacía podría hacer que final sea un identificador
final es siempre un
especificador en este caso

Véase también

override especificador (C++11) declara explícitamente que un método sobrescribe otro método
especificadores de propiedades de clase (C++26) final especificador (C++11) , reemplazabilidad (C++26) , reubicabilidad trivial (C++26)