Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redeclaring pure virtual function

Tags:

c++

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)?

like image 234
Sebastian Hoffmann Avatar asked Mar 03 '23 07:03

Sebastian Hoffmann


1 Answers

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.

like image 154
463035818_is_not_a_number Avatar answered Mar 08 '23 00:03

463035818_is_not_a_number