Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 294
felknight Avatar asked Jul 28 '15 20:07

felknight


People also ask

What is the purpose of delete operator?

The delete operator removes a given property from an object. On successful deletion, it will return true , else false will be returned.

Why destructor is used when delete is there?

When delete is used to deallocate memory for a C++ class object, the object's destructor is called before the object's memory is deallocated (if the object has a destructor). If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.

Why are virtual destructors needed?

Virtual destructors in C++ are used to avoid memory leaks especially when your class contains unmanaged code, i.e., contains pointers or object handles to files, databases or other external objects.

Does operator delete call the destructor?

The answer is yes. Destructor for each object is called. On a related note, you should try to avoid using delete whenever possible.


2 Answers

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.

like image 103
StenSoft Avatar answered Sep 20 '22 18:09

StenSoft


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)

like image 35
Artyer Avatar answered Sep 20 '22 18:09

Artyer