I recently came across code of the following kind:
struct A {
virtual void foo() = 0;
};
struct B : public A {
virtual void foo() = 0;
};
struct C : public B {
virtual void foo() override {
}
};
Is there any purpose in redeclaring a pure virtual method?
Even more importantly: Does this change semantics? I.e is B::foo
shadowing A::foo
and introducing a new vftable?
Whats happens in the above example if A::foo
is actually not pure virtual but provides an implementation (still compiles for me)?
Look at the example from cppreference:
struct Abstract { virtual void f() = 0; // pure virtual }; // "Abstract" is abstract struct Concrete : Abstract { void f() override {} // non-pure virtual virtual void g(); // non-pure virtual }; // "Concrete" is non-abstract struct Abstract2 : Concrete { void g() override = 0; // pure virtual overrider }; // "Abstract2" is abstract int main() { // Abstract a; // Error: abstract class Concrete b; // OK Abstract& a = b; // OK to reference abstract base a.f(); // virtual dispatch to Concrete::f() // Abstract2 a2; // Error: abstract class (final overrider of g() is pure) }
Is there any purpose in redeclaring a pure virtual method?
There is a purpose in declaring a virtual method as pure in a derived class: Abstract2
can decalre g
to be pure virtual even if it is not pure in Concrete
.
Does this change semantics? I.e is B::foo shadowing A::foo and introducing a new vftable?
No.
Whats happens in the above example if A::foo is actually not pure virtual but provides an implementation (still compiles for me)?
See the example.
You do not have to delcare a method as virtual
in a derived class. When it is declared virtual
in the base it is also virtual
in derived classes. In a nutshell, the =0
just tells the compiler that there is not necessarily a definition and that the class is abstract. Your example is the same as:
struct AA { // abstract
virtual void foo() = 0;
};
struct BB : public AA { }; // abstract
struct CC : public BB { // concrete
void foo() override {}
};
The only difference I am aware of is the type of BB::foo
:
// above
std::cout << std::is_same< decltype(&BB::foo), void (AA::*)()>::value;
std::cout << std::is_same< decltype(&BB::foo), void (BB::*)()>::value;
std::cout << std::is_same< decltype(&BB::foo), void (CC::*)()>::value << "\n";
// your example
std::cout << std::is_same< decltype(&B::foo), void (A::*)()>::value;
std::cout << std::is_same< decltype(&B::foo), void (B::*)()>::value;
std::cout << std::is_same< decltype(&B::foo), void (C::*)()>::value << "\n";
Output is:
100
010
That is, redeclaring foo
in B
introduces the name foo
directly in B
. B::foo
is a member function of B
. On the other hand in my example BB::foo
is the method from AA
. However, this doesn't affect C::foo
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With