What's the point of a final virtual function?
Typically final
will not be used on the base class' definition of a virtual function. final
will be used by a derived class that overrides the function in order to prevent further derived types from further overriding the function. Since the overriding function must be virtual normally it would mean that anyone could override that function in a further derived type. final
allows one to specify a function which overrides another but which cannot be overridden itself.
For example if you're designing a class hierarchy and need to override a function, but you do not want to allow users of the class hierarchy to do the same, then you might mark the functions as final in your derived classes.
Since it's been brought up twice in the comments I want to add:
One reason some give for a base class to declare a non-overriding method to be final is simply so that anyone trying to define that method in a derived class gets an error instead of silently creating a method that 'hides' the base class's method.
struct Base {
void test() { std::cout << "Base::test()\n"; }
};
void run(Base *o) {
o->test();
}
// Some other developer derives a class
struct Derived : Base {
void test() { std::cout << "Derived::test()\n"; }
};
int main() {
Derived o;
o.test();
run(&o);
}
Base
's developer doesn't want Derived
's developer to do this, and would like it to produce an error. So they write:
struct Base {
virtual void test() final { ... }
};
Using this declaration of Base::foo()
causes the definition of Derived to produce an error like:
<source>:14:13: error: declaration of 'test' overrides a 'final' function
void test() { std::cout << "Derived::test()\n"; }
^
<source>:4:22: note: overridden virtual function is here
virtual void test() final { std::cout << "Base::test()\n"; }
^
You can decide if this purpose is worthwhile for yourself, but I want to point out that declaring the function virtual final
is not a full solution for preventing this kind of hiding. A derived class can still hide Base::test()
without provoking the desired compiler error:
struct Derived : Base {
void test(int = 0) { std::cout << "Derived::test()\n"; }
};
Whether Base::test()
is virtual final
or not, this definition of Derived
is valid and the code Derived o; o.test(); run(&o);
behaves exactly the same.
As for clear statements to users, personally I think just not marking a method virtual
makes a clearer statement to users that the method is not intended to be overridden than marking it virtual final
. But I suppose which way is clearer depends on the developer reading the code and what conventions they are familiar with.
Does it make sense to add final keyword to the virtual function in a class that has no base class (is not derived)
If you don't mark the function as virtual
and final
then the child-class can still implement the function and hide the base-class function.
By making the function virtual
and final
the child-class can not override or hide the function.
What is the purpose of the final keyword in C++11 for functions?
What you are missing, as idljarn already mentioned in a comment is that if you are overriding a function from a base class, then you cannot possibly mark it as non-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!
};
Virtual function efficiency and the 'final' keyword
If fn
is defined as final
in Bar
, the compiler can dispatch calls to fn
through a pointer or reference to Bar
statically since it knows that Bar::fn
is the final overrider. For example, this program fragment:
struct Foo {
virtual void fn();
};
struct Bar : Foo {
void fn() final override;
};
void with_foo(Foo& o) { o.fn(); }
void with_bar(Bar& o) { o.fn(); }
compiles to (See gcc.godbolt.org for details):
with_foo(Foo&):
subq $8, %rsp
movq (%rdi), %rax
call *(%rax)
addq $8, %rsp
ret
with_bar(Bar&):
subq $8, %rsp
call Bar::fn()
addq $8, %rsp
ret
the call in with_foo
is dynamically dispatched (call *(%rax)
is an indirect call) through the vtable, but the call in with_bar
statically dispatches to Bar::fn()
.
The simplest method to make Bar::fn
be the final overrider of Foo::fn
without changing behavior is to define it to statically call Foo::fn
:
struct Bar : Foo {
void fn() final override { Foo::fn(); }
};
Does final imply override?
final
does not require the function to override anything in the first place. Its effect is defined in [class.virtual]/4 as
If a virtual function
f
in some classB
is marked with the
virt-specifierfinal
and in a classD
derived fromB
a functionD::f
overridesB::f
, the program is ill-formed.
That's it. Now override final
would simply mean
„This function overrides a base class one (override
) and cannot be overriden itself (final
).“final
on it's own would impose a weaker requirement.override
and final
have independent behavior.
Note that final
can only be used for virtual functions though - [class.mem]/8
A virt-specifier-seq shall appear only in the declaration of a
virtual member function (10.3).
Hence the declaration
void foo() final;
Is effectively the same as
virtual void foo() final override;
Since both require foo
to override something - the second declaration by using override
, and the first one by being valid if and only if foo
is implicitly virtual, i.e. when foo
is overriding a virtual function called foo
in a base class, which makes foo
in the derived one automatically virtual. Thus override
would be superfluous in declarations where final
, but not virtual
, occurs.
Still, the latter declaration expresses the intent a lot clearer and should definitely be preferred.
Why adding final to a member function of a final class?
final
on a virtual
function in a final
derived class is redundant.
Just like saying virtual
on a method marked override
is redundant. C++ just is that way sometimes.
Related Topics
What Does "#Define Str(A) #A" Do
What Is the Vtable Layout and Vtable Pointer Location in C++ Objects in Gcc 3.X and 4.X
Checking for Underflow/Overflow in C++
Convert String with Explicit Escape Sequence into Relative Character
Why Doesn't C++ Move Construct Rvalue References by Default
Sizeof(Struct) Returns Unexpected Value
How to Read a JSON File Containing Multiple Root Elements
Changing the Directory from Inside a C Program Under Windows Using System Command
C++ Template Parameter Type Inference
Familiar Template Syntax for Generic Lambdas
Are Class Members Guaranteed to Be Contiguous in Memory
Does Caffe Need Data to Be Shuffled
How to Create a Type in C++ That Takes Less Than One Byte of Memory
Is a Logical Right Shift by a Power of 2 Faster in Avr
Setting Roi with Mouse from a Rectangle on a Video
Possible Causes for Boost Not Being Found by Cmake in Certain Situations