tags:

views:

210

answers:

7

Possible Duplicate:
Why am I able to make a function call using an invalid class pointer

class B
{
    public:
    int i;
    B():i(0){}
    void func1()
    {
        cout<<“func1::B\n”;
    }
    void func2()
    {
        cout<<“i = “<<i;
    } 
};


int main()
{

    B *bp = new B;

    bp->func1();

    delete bp;

    bp = NULL;

    bp->func1();
    bp->func2();

    return 1; 
}

Output:

func1::B 
func1::B 
Runtime Exception:
NULL pointer access
+1  A: 

What's so interresting? The first output is correct. The second undefined behaviour. The third is an attempt to access information contained within a class which now does not exist. The program correctly recognizes it's a NULL pointer.

wheaties
A: 

Not an expert opinion but just looking over this:

bp->func1() doesn't require any access to class members - in Java this would be best defined as a static. Maybe the compiler is inlining the function?

jelford
A: 

You are deleting bp and then trying to call from it, because there is no object there, and in fact you have made the reference null there is a runtime exception.

Think about it this way:

You have told the compiler that no space is needed for any instance, therefor when it tries to use the variable 'i', its not stored anywhere meaningful and the program fails. Because you have made the reference null, the program catching it early before it does this.

Tomas Cokis
+5  A: 

When the code is fixed up to compile, the result is reasonably expected.

The func1() does not reference any member of the class, so it doesn't spot that it has a null pointer to work with as this; func2() does reference this->i and hence fails when this is null.

Strictly, it is undefined behaviour - anything could happen. But a core dump or runtime exception is one of the most common responses to that particular error.

Jonathan Leffler
+1 Indeed. After the object has been deleted, any access to its original members will cause an unexpected behavior, which may vary depending on the compiler you are using.
karlphillip
+6  A: 

This is the same old story of NULL (or invalid) object pointers; for the standard, calling a method on a NULL object pointer results in undefined behavior, which means that, as far as the standard is concerned, it could work perfectly fine, it could blow up the computer or kill some random people.

What happens here is a consequence of the typical implementation of classes by C++ compilers: classes usually are actually structures that contain just the fields, and all the methods are actually functions which take as a hidden parameter the this pointer.

Now, in this kind of implementation if you call a method with a NULL this pointer, if it doesn't access any of the fields it won't actually dereference this, so it should run fine (as happens with func1).

If, instead, the method tries to access any of the fields (e.g. func2), it will dereference the this pointer, which, being NULL, will lead to a crash (dereferencing a NULL pointer it's, again, undefined behavior, but usually it results in a crash).

Note that if the methods that you're calling are virtual it's almost sure that calling them with a NULL this pointer will lead to a crash, since the virtual calls are resolved via the vtable (a function pointer array), which is hidden at the beginning of the class.

By the way, void main() is not standard; it should be int main() (argv and argc are, instead, optional).

Matteo Italia
@Matteo, Thanks I found your explanation most useful to help me understand what was interesting in the snippet.
Als
A: 

I think that strictly the second call to bp->func1() should already crash but by the ouput it is called still using the bp that was allocated (but now is NULL), maybe some buggy compiler optimization (?). Then inside the bp->func1() call it really crashes because the member i is not accessible.

Juan
A: 

From the point where you use the pointer after having used delete, you enter "undefined behaviour" land.

So any behaviour beyond this point is compiler-implementation-dependant and have to be considered wrong even if it "looks like it works".

Klaim