16

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
{
};
Arun
  • 3,138
  • 4
  • 30
  • 41
  • 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. – BoBTFish Jan 14 '14 at 09:01
  • 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 ~Base – Arun Jan 14 '14 at 09:09
  • @Arun 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;`). – Alexis Wilke Feb 17 '21 at 01:19

4 Answers4

16

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.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 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.. – Arun Jan 14 '14 at 09:17
  • 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 ? – undu Jan 14 '14 at 09:20
  • 1
    @undu: `Derived` isn't abstract - its implicit destructor overrides the pure virtual one. – Mike Seymour Jan 14 '14 at 09:21
  • @MikeSeymour true, not part of the answer. – Luchian Grigore Jan 14 '14 at 09:24
  • 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. – Lightness Races in Orbit Jan 14 '14 at 09:42
  • @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. – Luchian Grigore Jan 14 '14 at 09:47
  • @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. :) – Lightness Races in Orbit Jan 14 '14 at 09:54
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.
Spook
  • 25,318
  • 18
  • 90
  • 167
4

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());
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
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.

henke37
  • 19
  • 6