views:

1150

answers:

7
class Widget
{
    public:
        Widget() {
            cout<<"~Widget()"<<endl;
        }
        ~Widget() {
            cout<<"~Widget()"<<endl;
        }

    void* operator new(size_t sz) throw(bad_alloc) {
        cout<<"operator new"<<endl;
        throw bad_alloc();
    }

    void operator delete(void *v) {
        cout<<"operator delete"<<endl;
    }

};

int main() 
{
    Widget* w = 0;
    try {
        w = new Widget();
    }
    catch(bad_alloc) {
        cout<<"Out of Memory"<<endl;
    }

    delete w;
    getch();
    return 1;
}

In this code, delete w does not call the overloaded delete operator when the destructor is there. If the destructor is omitted, the overloaded delete is called. Why is this so?

Output when ~Widget() is written
operator new
Out of Memory

Output when ~Widget() is not written
operator new
Out of Memory
operator delete

+3  A: 

I don't have a good answer, but I have simplified the issue slightly. The following code removes the operator new and exception handling:

#include <iostream>
using namespace std;

class Widget {

  public:
    Widget() {
     cout<<"Widget()"<<endl;
    }
    ~Widget() {
        cout<<"~Widget()"<<endl;
    }

  void operator delete(void *v) {
       cout << "operator delete" << endl;
  }
};

int main() {
    Widget* w = 0;
    cout << "calling delete" << endl;
    delete w;
}

This still exhibits the same behaviour and des so on both VC++ and g++.

Of course, deleting a NULL pointer is a no-op, so the compiler does not have to call operator delete. If one actually allocates an object:

    Widget* w = new Widget;

then things work as expected.

anon
A: 

You were trying to delete a NULL pointer. So, the destructor was not getting called.

class Widget {
public:
Widget() {
cout<<"Widget()"< }

~Widget() 
{          
 cout<<"~Widget()"<<endl;    
}    

void* operator new(size_t sz) throw(bad_alloc) 
{      
 cout<<"operator new"<<endl;  
 return malloc(sizeof(Widget));
 //throw bad_alloc();    
}  

void operator delete(void *v)
{         
 cout<<"operator delete"<<endl;   
}

};

int main() {

Widget* w = NULL; 
try 
{   
 w = new Widget();
 //throw bad_alloc();
}   
catch(bad_alloc) 
{        
 cout<<"Out of Memory"<<endl;  
}   
delete w;

}

Output: operator new Widget() ~Widget() operator delete

Jagannath
Yes, but the question is, why does his custom delete get called when delete is called with NULL? (But only if the class has no destructor?)
Daniel Earwicker
The question is not why the destructor is not called. The question is rather that when i dont provide a destructor (in which case the compiler creates one for me), why does the overloaded delete gets called??
s_itbhu
+16  A: 

I remember something similar on operator delete a while ago in comp.lang.c++.moderated. I cannot find it now, but the answer stated something like this ..

Unfortunately, the language specification is not sufficiently clear on whether the control should go into the overloaded 'operator delete' when the delete-expression is invoked on the null-pointer of corresponding type, even though the standard does say that delete-expression on null-pointer is a no-op.

And James Kanze specifically said:

It's still the responisiblity of operator delete (or delete[]) to check; the standard doesn't guarantee that it won't be given a null pointer; the standard requires that it be a no-op if given a null pointer. Or that the implementation is allowed to call it. According to the latest draft, "The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect." I'm not quite sure what the implications of that "is one supplied in the standard library" are meant to be---taken literally, since his function is not one provided by the standard library, the sentence wouldn't seem to apply. But somehow, that doesn't make sense

I remember this becoz i had a similar prob sometime back and had preserved the answer in a .txt file.

UPDATE-1:

Oh i found it here. Also read this link defect report. So, the answer is Unspecified. Chapter 5.3.5/7.

Abhay
+1 sounds like as close to an answer as we'll get... though I await further developments with bated breath.
Daniel Earwicker
If it has to be a no-op, the compiler may not execute any user code, since that code can result in side effects (i.e. doing a print thingie)
xtofl
+4  A: 

The reason is that if you have a destructor, the call to the delete operator is done from within the scalar deleting destructor, which in VC contains the call to both your destructor and the delete operator. The compiler provides code that checks whether you're trying to delete a NULL pointer. Deleting such pointer is legal, of course, but the destructor of such object must not be invoked, as it might contain usage of member variables. For that the call to the scalar deleting destructor is avoided, and as a result the call to the delete operator is avoided as well.

When there is no destructor, the compiler just calls the delete operator directly, without generating the scalar deleting destructor. Therefore, in such cases the delete operator is invoked after all.

eran
This isn't quite correct - `delete` quite obviously cannot be done from within destructor, because a destructor would be called for objects of that class with static and auto storage as well, and calling `delete` on those is obviously undesirable.What happens instead is that VC++ generates an _extra_ function called "scalar deleting destructor", which first calls the destructor proper (which is still generated as another, separate function), and then performs `delete this`. And then calls that scalar deleting destructor wherever you do `delete` on objects of that type.
Pavel Minaev
Thank you for the correction, Pavel. Fixed my answer accordingly.
eran
I think this is the correct answer as it's supported by the fact that if the dtor is removed from Widget, but an object with a dtor is added, you'll get the same behavior.
Andreas Magnusson
But if the user does not provide a dtor, the compiler as per the standard *provides* the default dtor. Does this mean that the compiler-generated-default dtor always calls operator-delete and only in case of user-provided dtor does check for null pointer? Confusing IMHO...
Abhay
The compiler "provides" a generated destructor in a sense that you can reference it in the code. But since such a destructor would do completely nothing for the sample type (no vtable, no base class, and no members requiring destruction), it's effectively optimized to a no-op, and "scalar deleting destructor" isn't even generated.
Pavel Minaev
Also note that this answer is correct for VC++. The original question is also about g++.
Pavel Minaev
@Abhay: As I said if you let the compiler provide the dtor for Widget *and* let Widget hold an instance of an object (say std::string) that has a dtor (i.e. requires destruction), you will get the same behavior as if you had provided a dtor for Widget. But if there's nothing to destruct for Widget then it's safe for the compiler to call the user provided delete in the case of a null-ptr.
Andreas Magnusson
A: 

The object destructor is called before the delete operator. So my guess would be that it tries to call the destructor, realizes that the pointer is NULL therefore

  1. doesn't call destructor which needs an instance
  2. stops the deleteing operation there (kind of speed optimization IMHO).

As Neil said, if w contains a Widget, it should work.

streetpc
If it doesn't call the destructor, then the same behavior should be shown when I don't write a destructor (in which case the compiler should create one for me). The main point here is why the behavior is different in the 2 cases
s_itbhu
Because when the destructor is not specified the delete operator is directly invoked (default behavior), while when it is specified, there is this "pre-check" of the instance I'm stating here. Here I'm explaining the case _with_ the destructor, the comparison was implied - maybe not clearly enough, though.
streetpc
+9  A: 

First of all, this can really be simplified down to delete (Widget*)0 - everything else in your main() is unnecessary to repro this.

It's a code generation artefact that happens because 1) user-defined operator delete must be able to handle NULL values, and 2) compiler tries to generate the most optimal code possible.

First let's consider the case when no user-defined destructor is involved. If that's the case, there's no code to run on the instance, except for operator delete. There's no point in checking for null before transferring control to operator delete, because the latter should do a check anyway; and so the compiler just generates unconditional call of operator delete (and you see the latter print a message).

Now the second case - destructor was defined. This means that your delete statement actually expands into two calls - destructor, and operator delete. But destructor cannot be safely called on a null pointer, because it could try to access class fields (the compiler could figure out that your particular destructor doesn't really do it and so is safe to call with null this, but looks like they don't bother in practice). So it inserts a null check in there before the destructor call. And once the check is already there, it might as well use it skip the call to operator delete, too - after all it's required to be a no-op anyway, and it will spare an extra meaningless check for null inside operator delete itself in case the pointer actually is null.

So far as I can see, nothing in this is in any way guaranteed by ISO C++ spec. It's just that both compilers do the same optimization here.

Pavel Minaev
Nice explanation! Imho, there's a third case, too: virtual destructor defined. This one cannot be found at runtime if the pointer is NULL. Yet another reason why delete of NULL pointers can't call destructor code...
xtofl
+3  A: 

Would like a leave a comment, instead of answer, didn't have enough privileges being a new member.

An exception is being raised during the creation of object. The destructor is not getting called, as the object itself is not created.

That you can also observe, as the messages from the constructor & destructor are not getting displayed.

But, the delete is being called when the destructor is not defined. If thought in the directon that when destrcutor is not defined, C++ Compiler considers it as any other operator, the compiler by default provides a destructor when not defined.

Narendra N
That is definitely simple.
Daniel Earwicker
And a good point, too!
xtofl