Why a pure virtual destructor needs an implementation
Asked Answered
R

4

19

I know the cases where pure virtual destructors are needed. I also know that If we don't provide an implementation for them it will give me a linker error. What I don't understand is why this should be the case in a code fragment as shown below:

int main()
{
    Base * p = new Derived;
}

Here there is no delete, so no call to destructor and so no need for its implementation(assuming it is supposed to behave like other normal functions which are declared but not defined, linker complains only when we call them)...or am I missing something?

I need to understand why this should be a special case?

Edit: based on comments from BoBTFish

Here are my Base and Derived classes

class Base
{
public:
    Base(){}
    virtual ~Base() = 0;
};

class Derived : public Base
{
};
Retha answered 14/1, 2014 at 9:0 Comment(3)
You don't show how you write Base and Derived, but I guess what is happening is the compiler is generating the destructor for Derived, which will try to call the destructor for Base, which does not exist.Unsling
It does compile...only linker complains... try it.. I am using VS2012 and I am pretty confident this shouldn't be compiler dependent...and the errors go away once I give an implementation for the ~BaseRetha
@Retha It compiles because at compile time it doesn't know where the Base::~Base() function is going to be implemented. As for Derived::~Derived(), since you did not defined it, it's automatically generated (equivalent to ~Derived() = default;).Orestes
U
18

The compiler tries to build the virtual table given a virtual (pure or not) destructor, and it complains because it can't find the implementation.

virtual destructors differ from other virtual functions because they are called when the object is destroyed, regardless of whether it was implemented or not. This requires the compiler to add it to the vf table, even if it's not called explicitly, because the derived class destructor needs it.

Pedantically, the standard requires a pure virtual destructor to be implemented.

Unionize answered 14/1, 2014 at 9:4 Comment(7)
Accepting this answer as compiler needing the definition for Vf table makes sense and is consistent with other virtual functions... All virtual functions seem to require a body for object creation even if they are not called, I just tried it..Retha
Still, I don't understand why this code compiles (only linker fails to find a reference to ~Base()). Why does 'new Derived' works even if Derived is abstract ?Tera
@undu: Derived isn't abstract - its implicit destructor overrides the pure virtual one.Salyer
@MikeSeymour true, not part of the answer.Unionize
I don't think it's "pedantic" to quote the rules of the language. Those rules are god. The practical ramifications only follow on from them.Warfold
@LightnessRacesinOrbit given "I know the cases where pure virtual destructors are needed. I also know that If we don't provide an implementation for them it will give me a linker error" I assumed he already knew the rule, so I wasn't overly concerned with re-iterating it.Unionize
@LuchianGrigore: The end of the same paragraph says "What I don't understand is why this should be the case in a code fragment as shown below", which means that despite his assurances, his understanding was incorrect. So I was very concerned with updating it. :)Warfold
R
8

C++11 standard:

12.4 Destructors

Paragraph 9:

A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
Rusert answered 14/1, 2014 at 9:4 Comment(2)
I think he's asking why the linker generates an error in this case, and not for, say, pure virtual functions.Unionize
@LuchianGrigore As I see, you explained it quite nicely, so I only added C++ standard reference :)Rusert
W
5

Destructors differ from other virtual functions in this way, because they are special and automatically invoked in bases, with no possible, useful or meaningful way to prevent it.

[C++11: 12.4/9]: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

Bases are always destroyed, and to do this, a base destructor definition is required. Conversely, other overridden virtual functions are not invoked automatically at all. Hence the special-case requirement.

struct Base
{
   virtual ~Base()    = 0;  // invoked no matter what
   virtual void foo() = 0;  // only invoked if `Base::foo()` is called
};

Base::~Base() {}
/* void Base::foo() {} */

struct Derived : Base
{
   virtual void foo() { /* Base::foo(); */ }
};

int main()
{
    std::unique_ptr<Base> ptr(new Derived());
}
Warfold answered 14/1, 2014 at 9:33 Comment(0)
U
0

One practical reason is that destructors come first in the list of virtual member functions in the vtable in practically all implementations. And implementations tend to define the vtable itself when it defines the first virtual member function. So, no destructor, no vtable. And the vtable is critical.

Unexceptional answered 18/7, 2020 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.