I came across the following code recently:
class Foo
{
public:
void bar();
// .. other stuff
};
void Foo::bar()
{
if(!this) {
// .. do some stuff without accessing any data members
return;
}
// .. do normal actions using data members
}
The code compiles because in C++ methods are just functions that are implicitly passed a pointer for 'this' and 'this' can be checked to be NULL just like any other pointer. Obviously this code is confusing and bad practice even though it doesn't crash; it would be pretty confusing to step through the code in the debugger, see a NULL pointer is about to have a method called on it and then not see the expected crash. My question is: does it violate the C++ standard to call SomeFooPtr->bar()
where SomeFooPtr == NULL
?
It occurs to me that it may not because the user defined operator-> returns a pointer, which means that even if that pointer is NULL it definitely hasn't been dereferenced (dereferencing a NULL pointer I'm sure is regarded by the standard as illegal or undefined). On the other hand the semantics of raw pointers don't necessarily have to match the semantics of user defined pointers -- perhaps operator-> on them is considered a dereference even though the compiler won't generate one.
This will probably work on most systems, but it is Undefined Behaviour. Quoth the Standard:
And:
Evaluation of
*x
wherex
is a null pointer results in Undefined Behaviour, so yours is clearly a case of UB, before the function is even entered.It is UB. A good way to make it crash is to use it as a base class of a derived class that uses multiple inheritance. YMMV.
It doesn't matter if it is legal, it is confusing to the reader. In the implementation where this code works, the vtable is being accessed by type, certainly not by object.
Moreover, I expect that this code was put in to cover for a constructor failure, which would mask a variety of problems elsewhere. Constructor failure should be handled correctly, not with a nasty kludge like the example.
This is (all together now) undefined behavior. However, for many compilers it will work, with the additional constraint that the method must be non-virtual.
This test is broken even if dereferencing was not an UB. It breaks when this-adjustments for multiple inheritance come into the play:
prints "safe" here.