statement method español c++ c++11 final

method - ¿Cuál es el propósito de la palabra clave "final" en C++ 11 para las funciones?



override statement c++ (9)

"final" también permite que una optimización del compilador omita la llamada indirecta:

class IAbstract { public: virtual void DoSomething() = 0; }; class CDerived : public IAbstract { void DoSomething() final { m_x = 1 ; } void Blah( void ) { DoSomething(); } };

con "final", el compilador puede llamar a CDerived::DoSomething() directamente desde dentro de Blah() , o incluso en línea. Sin él, tiene que generar una llamada indirecta dentro de Blah() porque Blah() podría llamarse dentro de una clase derivada que ha anulado DoSomething() .

¿Cuál es el propósito de la palabra clave final en C ++ 11 para las funciones? Entiendo que previene el reemplazo de funciones por clases derivadas, pero si este es el caso, ¿no es suficiente declarar sus funciones final como no virtuales? ¿Hay algo más que me falta aquí?


Final no se puede aplicar a funciones no virtuales.

error: only virtual member functions can be marked ''final''

No sería muy significativo poder marcar un método no virtual como ''final''. Dado

struct A { void foo(); }; struct B : public A { void foo(); }; A * a = new B; a -> foo(); // this will call A :: foo anyway, regardless of whether there is a B::foo

a->foo() siempre llamará a A::foo .

Pero, si A :: foo fuera virtual , B :: foo lo anularía. Esto podría ser indeseable y, por lo tanto, tendría sentido hacer que la función virtual sea definitiva.

La pregunta es, ¿por qué permitir el final en las funciones virtuales? Si tienes una jerarquía profunda:

struct A { virtual void foo(); }; struct B : public A { virtual void foo(); }; struct C : public B { virtual void foo() final; }; struct D : public C { /* cannot override foo */ };

Luego, la final pone un ''piso'' sobre la cantidad de anulación que se puede hacer. Otras clases pueden extender A y B y anular su foo , pero una clase se extiende C entonces no está permitido.

Por lo tanto, probablemente no tenga sentido hacer que el foo de ''nivel superior'' sea final , pero podría tener sentido más abajo.

(Creo que, sin embargo, hay espacio para extender las palabras final y anular a los miembros no virtuales. Sin embargo, tendrían un significado diferente).


La palabra clave final en C ++ cuando se agrega a una función, evita que sea anulada por una clase base. Además, cuando se agrega a una clase evita la herencia de cualquier tipo. Considere el siguiente ejemplo que muestra el uso del especificador final. Este programa falla en la compilación.

#include <iostream> using namespace std; class Base { public: virtual void myfun() final { cout << "myfun() in Base"; } }; class Derived : public Base { void myfun() { cout << "myfun() in Derived/n"; } }; int main() { Derived d; Base &b = d; b.myfun(); return 0; }

También:

#include <iostream> class Base final { }; class Derived : public Base { }; int main() { Derived d; return 0; }


La palabra clave final le permite declarar un método virtual, anularlo N veces, y luego ordenar que ''esto ya no pueda ser anulado''. Sería útil para restringir el uso de su clase derivada, para que pueda decir "Sé que mi superclase le permite anular esto, pero si quiere derivar de mí, ¡no puede!".

struct Foo { virtual void DoStuff(); } struct Bar : public Foo { void DoStuff() final; } struct Babar : public Bar { void DoStuff(); // error! }

Como otros carteles señalaron, no se puede aplicar a funciones no virtuales.

Un objetivo de la palabra clave final es evitar la anulación accidental de un método. En mi ejemplo, DoStuff () puede haber sido una función auxiliar que la clase derivada simplemente necesita cambiar el nombre para obtener el comportamiento correcto. Sin final, el error no se descubriría hasta la prueba.


Lo que te estás perdiendo, como idljarn ya mencionado en un comentario es que si anulas una función de una clase base, entonces no puedes marcarla como no virtual:

struct base { virtual void f(); }; struct derived : base { void f() final; // virtual as it overrides base::f }; struct mostderived : derived { //void f(); // error: cannot override! };


Nada que agregar a los aspectos semánticos de "final".

Pero me gustaría añadir al comentario de chris green que "final" podría convertirse en una técnica de optimización de compiladores muy importante en un futuro no muy lejano. No solo en el caso simple que mencionó, sino también en jerarquías de clase del mundo real más complejas que pueden "cerrarse" por "final", lo que permite a los compiladores generar un código de despacho más eficiente que con el enfoque vtable habitual.

Una desventaja clave de los vtables es que, para cualquier objeto virtual (suponiendo 64 bits en una CPU Intel típica), el puntero solo consume un 25% (8 de 64 bytes) de una línea de caché. En el tipo de aplicaciones que me gusta escribir, esto me duele mucho. (Y desde mi experiencia, es el argumento # 1 en contra de C ++ desde un punto de vista de rendimiento purista, es decir, por programadores de C).

En aplicaciones que requieren un rendimiento extremo, que no es tan inusual para C ++, esto podría ser realmente impresionante, sin necesidad de solucionar este problema manualmente en estilo C o malabarismos de plantillas extrañas.

Esta técnica se conoce como Devirtualization . Un término que vale la pena recordar. :-)

Hay un gran discurso reciente de Andrei Alexandrescu que explica muy bien cómo puede solucionar estas situaciones hoy y cómo "final" podría ser parte de la solución de casos similares "automáticamente" en el futuro (discutido con los oyentes):

http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly


Un caso de uso para la palabra clave ''final'' que me gusta es el siguiente:

// This pure abstract interface creates a way // for unit test suites to stub-out Foo objects class FooInterface { public: virtual void DoSomething() = 0; private: virtual void DoSomethingImpl() = 0; }; // Implement Non-Virtual Interface Pattern in FooBase using final // (Alternatively implement the Template Pattern in FooBase using final) class FooBase : public FooInterface { public: virtual void DoSomething() final { DoFirst(); DoSomethingImpl(); DoLast(); } private: virtual void DoSomethingImpl() { /* left for derived classes to customize */ } void DoFirst(); // no derived customization allowed here void DoLast(); // no derived customization allowed here either }; // Feel secure knowing that unit test suites can stub you out at the FooInterface level // if necessary // Feel doubly secure knowing that your children cannot violate your Template Pattern // When DoSomething is called from a FooBase * you know without a doubt that // DoFirst will execute before DoSomethingImpl, and DoLast will execute after. class FooDerived : public FooBase { private: virtual void DoSomethingImpl() {/* customize DoSomething at this location */} };


final agrega una intención explícita de no tener su función anulada, y causará un error de compilación si esto se viola:

struct A { virtual int foo(); // #1 }; struct B : A { int foo(); };

A medida que el código permanece, se compila y B::foo anula A::foo . B::foo también es virtual, por cierto. Sin embargo, si cambiamos # 1 a virtual int foo() final , entonces este es un error del compilador, y no podemos anular A::foo en las clases derivadas.

Tenga en cuenta que esto no nos permite "volver a abrir" una nueva jerarquía, es decir, no hay manera de hacer que B::foo una función nueva e independiente que pueda estar independientemente al frente de una nueva jerarquía virtual. Una vez que una función es definitiva, nunca se puede volver a declarar en ninguna clase derivada.


  • Es para evitar que una clase sea heredada. De la Wikipedia :

    C ++ 11 también agrega la capacidad de evitar heredar de las clases o simplemente prevenir los métodos de anulación en las clases derivadas. Esto se hace con el identificador especial final. Por ejemplo:

    struct Base1 final { }; struct Derived1 : Base1 { }; // ill-formed because the class Base1 // has been marked final

  • También se usa para marcar una función virtual a fin de evitar que se anule en las clases derivadas:

    struct Base2 { virtual void f() final; }; struct Derived2 : Base2 { void f(); // ill-formed because the virtual function Base2::f has // been marked final };

Wikipedia además hace Wikipedia :

Tenga en cuenta que ni la override ni la final son palabras clave del lenguaje. Son identificadores técnicos; solo adquieren un significado especial cuando se usan en esos contextos específicos . En cualquier otra ubicación, pueden ser identificadores válidos.