Search code examples
c++virtualgnu

Implicit virtual vs explicit virtual in inherited classes at compile time


TL;DR Old code I am referencing is not compliant with C++. It's amazing the code ever worked. You cannot call virtual methods in constructors and deconstructors.

A peer and I had a discussion about the virtual keyword in C++. On an older ubuntu OS I was working on a program that reported errors about calling "pure virtual methods" which should not have happened

class Base {
    virtual ~Base() { this->DoSomethingElse(); }
    virtual bool DoSomething() = 0;
    virtual bool DoSomethingElse() = 0;
};

class Foo : public Base {
    ~Foo();
    bool DoSomething();
    bool DoSomethingElse();
};

// Later...
Base* obj = new Foo();
obj->DoSomething(); // Pure virtual function called SIGABORT
delete obj; // Pure virtual function called SIGABORT

After stepping through a debugger, I finally I added virtual on the inherited classes

class Base {
    virtual ~Base() { this->DoSomethingElse(); }
    virtual bool DoSomething() = 0;
    virtual bool DoSomethingElse() = 0;
};

class Foo : public Base {
    virtual ~Foo();
    virtual bool DoSomething();
    virtual bool DoSomethingElse();
};

// Later...
Base* obj = new Foo();
obj->DoSomething(); // Ok!
delete obj; // Ok!

I double checked google for anything that could suggest virtual was needed on inherited classes before telling my peer. They said that yes, virtual is needed in the standard but that compilers automatically fill virtual in for inherited classes most of the time. From my understanding (and I believe most programmers) virtual is needed when you want to override that function via polymorphism. But it was not clear that you need to mark child class function implementations too.

I'm suprised by the lack of resources on the topic. So what is it? Is virtual implied in modern C++ compilers and where does the standard describe that?


Solution

  • A function that overrides a virtual function is virtual. That's not the problem here.

    The problem is the call to DoSomethingElse() (with or without the redundant this->) in the destructor for Base. When a constructor or destructor calls a virtual function it dispatches to the version of the function that belongs to the class whose constructor or destructor is being called, not to the version for a class derived from that class. So that call in the destructor calls Base::DoSomethingElse(), which is a call to a pure virtual function, and that's why the runtime aborts.