tags:

views:

903

answers:

8

I stumbled upon this piece of code

this seems totaly broken to me, but it does happen that "this" is null. I just don't get how this can be null

it is inside a normal method call such as

myObject->func();

inside MyObject::func() we have

if (!this) { return false; }

is there any way I can have the first line to throw a NullPointerException instead of going inside the null (???) method?

A: 

trust your intuition, you should! - delete!

BPAndrew
+1  A: 
if(this == null)
   throw new NullPointerException;
if(!this)
   return false;
samoz
Why would you throw a pointer to the exception?
Daniel Earwicker
And why test this twice for NULL?
Eclipse
Your code looks like C#, isn't it?
rstevens
"if (!this)" is not valid C# or Java - it could be C++ if you had "#define null 0" somewhere.
Daniel Earwicker
Also in C# and Java it would have to be "throw new NullPointerException()" - and that's a pre-defined exception in Java, but not in C# which has NullReferenceException instead.
Daniel Earwicker
+18  A: 

If you have:

MyObject *o = NULL;

o->func();

What happens next depends on whether func is virtual. If it is, then it will crash, because it needs an object to get the vtable from. But if it's not virtual the call proceeds with the this pointer set to NULL.

I believe the standard says this is "undefined behaviour", so anything could happen, but typical compilers just generate the code to not check whether the pointer is NULL. Some well known libraries rely on the behaviour I described: MFC has a function called something like SafeGetHandle that can be called on a null pointer, and returns NULL in that case.

You might want to write a reusable helper function:

void CheckNotNull(void *p)
{
    if (p == NULL)
        throw NullPointerException();
}

You can then use that at the start of a function to check all its arguments, including this:

CheckNotNull(this);
Daniel Earwicker
Just to clarify things... dereferencing NULL is undefined behavior by the standard. MFC really shouldn't be depending on this. If it were implementation defined, things would be different.Chances are that this is a poor attempt at "fixing" a race condition.
D.Shawley
i think this story is appropriate: http://www.gotw.ca/conv/002.htm
Johannes Schaub - litb
Alternative moral to the story: implement CheckNotNull and make it throw an exception with the message "Don't be an asshole, Bob!"
Daniel Earwicker
Seriously, there's a reason the CLR throws as soon as a method is called on a null pointer - because people often (accidentally) write code that doesn't respect the standard. The C++ approach is to let such code have undefined behaviour. If you want something better, put your own checks in.
Daniel Earwicker
I think it is better to assert(this != 0); than throw, since clearly it indicates a programmer error.
Brian Neal
Johannes Schaub - litb
Earwickler, there exactly is a reason the CLR throws. that is because the user code should not do that :) it should just do what it is written for, and not handle all possible cases where users mess with it in.
Johannes Schaub - litb
But it is possible for it to be true on certain platforms - it's just never valid to rely on it. I'm just explaining the principle of "fail fast" - better to fail in a defined way if you have the opportunity to do so. My personal approach is to use the CLR, where fail-fast is built in.
Daniel Earwicker
By the way, it's Earwicker with no L, but I like the sound of Earwickler and may change my name to that!
Daniel Earwicker
oops, i'm sorry. didn't notice there's no L in it :)
Johannes Schaub - litb
Shawley, there's nothing wrong with MFC relying on that behavior. The *standard* says it's undefined, but one of the liberties that implementations are allowed is to go "above and beyond" the standard's minimum requirements. Implementations may define things that the standard chose not to. Since MFC is designed and written to be used with compilers from just one vendor, the MFC folks can simply ask their MSVC colleagues what happens when you dereference a null pointer in MSVC, and then rely on it. Microsoft ensures the behavior stays in place as long as it supports both MSVC and MFC.
Rob Kennedy
Shawley not!.
Daniel Earwicker
A: 

this == null should only occur if you are calling a method on a deleted object, or if something is writing to memory that it should not (and overwriting the object's this pointer in particular).

You should investigate what is really wrong with your code rather than trying to work around it like this.

Tyler McHenry
Deleting an object doesn't set its pointer to null.
Mark Ransom
+1 for Mark Ransom's comment.
Tom
A: 

this pointer can become null in cases like these:

class Test
{

public:
    bool f();

private:
    int m_i;
};


bool Test::f()
{
    if(!this)
    {
     return false;
    }

    m_i = 0;
    return true;

}


int main(int argc, char **argv)
{
    Test* p = new Test;
    delete p;
    p = NULL;

    p->f();
}

I guess somebody did a quick hack to avoid to the access violation exception.

Naveen
I'm just surprised that that doesn't crash as soon as it tryies to call f... I mean if p is NULL, p->anything should seg-fault...
Brian Postow
+1  A: 

It's possible for this to be null. I suspect that this code is trying to (badly) detect a race condition, where the object is not yet finished being initialized, or has been deleted.

John Feminella
you can't change "this". it's an rvalue. (= "this" is not an object). you can think of it like this, imagine "self" is the this pointer: T *self_ = this; #define self (self_ + 0) // you couldn't do (self + 0) = ...
Johannes Schaub - litb
Ugh, you're right. I meant to say "but it can't be written to".
John Feminella
+1  A: 

(this == NULL) is undefined behaviour according to the standard. I think you should remove this check :)

Assume we make the following call:

((CSomeClass*) 0)->method();

The behaviour is already undefined, so why bother doing the check for this == NULL in CSomeClass::method ?

EDITED: I Assume that your compiler will handle (0 == this) if you don't use member variables, but where would it find the virtual table pointer? In this case, your class can not use polymoprhism.

bb
One reason for putting the check in is to enforce the standard properly, if your compiler doesn't do it for you.
Daniel Earwicker
i would not call it on NULL in the first place, then the check would not be needed at all. or - when i check - then at the call time. why should the callee worry when the caller does such impossible things? a good smart pointer that contains an assert for that situation is all right i think
Johannes Schaub - litb
yes, assert( obj ) in code ( not in method) or in smart pointer help us detect problem.in general I saw many developer who not though what thing should never happens and should be asserted and what thing should be checked.
bb
+2  A: 

A way to trap these kind of errors (by design) is using a pointer-wrapper class (resembling a shared_ptr) that throws upon creation with a null pointer argument. It may throw also when dereferenced, but that's a little late - better than nothing, I guess.

xtofl
i thought first boost::shared_ptr would throw. but after having looked it up, i had to delete my answer, because it's nothrow :( apparently it is up to the implementation whether something is thrown or not. but nevertheless, in my GCC i get an assertion failure - better than nothing :)
Johannes Schaub - litb