views:

196

answers:

2

I found this one question asking the same thing, however only the 'new' part was answered, so here goes again.

Why is the delete operator required to be static? Somehow it doesn't make sense. The new operator makes perfect sense, just like the constructor can't be virtual, neither can the new operator. However, the destructor can (and should) be virtual when you use inheritance, in order to allow destruction of objects being used (by way of polymorphism) as a base class.

I understand that, when the delete operator is called, the object has already been destroyed, so no 'this' exists. Yet it still makes sense, using the same reasoning as with virtual destructor, to have the delete operator match the new operator which created the object.

This is what I mean

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

class B : public A
{
  public:
    void* operator new (size_t sz);
    void  operator delete (void* ptr, size_t sz);
};

now if we do

A *ptr = new B();
delete ptr; // <-- fail

A's delete operator (default) should've been called, since it's static and it's not known (for anything but the trivial case here) at compile time which delete-operator is the correct one.

However, I made a small test program with the code above (just malloc/free in the new/delete operators, and print statement in delete), and compiled it using g++. Running it quite unexpectedly produced the output in B's delete operator.

My (real) question is this: Is there some implicit 'virtualness' to the delete operator? Is it only static in the no-this-pointer sense? Or is this just a g++ feature?

I started looking through the C++ specification, but I must admit, I was bit overwhelmed by it, so any help appreciated.

+1  A: 

delete operator is for deallocating memory only, and memory is deallocated for the most derived class object as a whole - in one action - exactly the same way as with new operator it is allocated for the whole most-derived class object - the object of class passed as argument into new Class construct.

This is why when you do delete ptr; the delete operator is always called only once for the actual most-derived class of the object being deleted and the data on what class it is is deduced from either the vtable if the virtual destructor is present or the type of the pointer if there's no virtual destructor. That's why there'no implicit virtualness to the delete operator - all virtualness ends at the point of destructor call.

sharptooth
@roe: The most derived class operator delete is called the same way as the most derived operator new is called. So if you have different banks you will get objects of different classes allocated from different banks, but only as whole objects, it will never happen that a sub-object is from one bank and the derived class part of the object is from another.
sharptooth
So the operator to use is deduced from what class the actual object is (i.e. it looks for a virtual table, or however it works), sounds like there's some implied virtualness to the operator? Assuming I have a class C deriving from B, `A* ptr = new C; delete ptr;` still calls B's delete operator, which sounds like it's pretty much virtual to me. Sorry for the comment spamming, I'm just trying to wrap my head around it.
roe
@roe: delete and new operators are inherited. Since you've overloaded them in class B class C will also use them. See the nice chart here: http://msdn.microsoft.com/en-us/library/c5at8eya.aspx
sharptooth
I guess what I'm trying to say is, the object itself must carry the information along which delete operator to use for that object, i.e. the delete operator is implicitly virtual. Do you happen to know where I can find this in the spec?
roe
@roe: No, I don't know where it is in the spec. I guess the key point here is using virtual destructors - if you have them all the rest works, otherwise it depends.
sharptooth
+6  A: 

The answer in the language rules is really in 12.5 [class.free].

If you are deleting via a pointer to a base class then the destructor must be virtual or you get undefined behaviour. Otherwise, the implementation has to determine the dynamic type of the object being deleted.

12.5/4 says that when the delete isn't prefixed by :: then the deallocation function is determined by looking up delete in the context of the dynamic type's virtual destructor. This ensures virtual-like lookup, even though operator delete is always a static member function.

Raw allocation and deallocation happen conceptually outside of the object's lifetime so by the time the deallocation function is to be called, there is no longer an object to provide a virtual lookup mechanism but the lookup rules ensure that operator delete has a dynamic (virtual-lite!) lookup mechanism. This means that operator delete can sensibly be static without losing touch with the original object's dynamic type.

Charles Bailey
Excellent, thanks! I was rummaging around in 3.7.3...
roe
There's also relevant stuff in 5.3.5 as well as 3.7.3 and 12.5. Although billed as a reference work, it seems that you do have to read it cover to cover otherwise you'd never know if there was a small paragraph relevant to something that you're looking up in a completely different section to where you were actually looking.
Charles Bailey