felknight
felknight

Reputation: 1423

Why is delete operator required for virtual destructors

In a freestanding context (no standard libraries, e.g. in operating system development) using g++ the following phenomenon occurs:

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

class Derived : public Base {
public:
    ~Derived() {}
};

int main() {
    Derived d;
}

When linking it states something like this: undefined reference to operator delete(void*)

Which clearly means that g++ is generating calls to delete operator even though there are zero dynamic memory allocations. This doesn't happen if destructor isn't virtual.

I suspect this has to do with the generated vtable for the class but I'm not entirely sure. Why does this happen?

If I must not declare a delete operator due to the lack of dynamic memory allocation routines, is there a work around?

EDIT1:

To successfully reproduce the problem in g++ 5.1 I used:

g++ -ffreestanding -nostdlib foo.cpp

Upvotes: 14

Views: 3469

Answers (2)

Artyer
Artyer

Reputation: 40826

In C++20 there is now a fix: P0722R3. The static void operator delete(T*, std::destroying_delete_t) deallocation function. It essentially maps to the destroying destructor.

You can just make it not call ::operator delete, like:

class Base {
public:
    void operator delete(Base* p, std::destroying_delete_t) {
        // Shouldn't ever call this function
        std::terminate();  // Or whatever abort-like function you have on your platform

        // The default implemenation without any overrides basically looks like:
        // p->~Base(); ::operator delete(p);
        // Which is why the call to `operator delete` is generated
    }
    virtual ~Base() {}
};

class Derived : public Base {
public:
    // Calls Base::operator delete in deleting destructor, so no changes needed
    ~Derived() {}
};

int main() {
    Derived d;
}

The deleting destructor is the one called when you do delete ptr_to_obj;. It can only be called by delete expressions, so if you have none in your code, this should be fine. If you do, you can replace them with ::delete ptr_to_obj; and the deleting destructor will no longer be called (it's purpose is to call overriden operator delete's for classes, and ::delete will only call the global ::operator delete)

Upvotes: 2

StenSoft
StenSoft

Reputation: 9609

Because of deleting destructors. That are functions that are actually called when you call delete obj on an object with virtual destructors. It calls the complete object destructor (which chains base object destructors — the ones that you actually define) and then calls operator delete. This is so that in all places where delete obj is used, only one call needs to be emitted, and is also used to call operator delete with the same pointer that was returned from operator new as required by ISO C++ (although this could be done more costly via dynamic_cast as well).

It's part of the Itanium ABI that GCC uses.

I don't think you can disable this.

Upvotes: 15

Related Questions