At least in the typical implementation, when you call a non-virtual member function via a pointer, that pointer is not dereferenced to find the function.
The type of the pointer is used to determine the scope (context) in which to search for the name of the function, but that happens entirely at compile time. What the pointer points at (including "nothing", in the case of a null pointer) is irrelevant to finding the function itself.
After the compiler finds the correct function, it typically translates a call like a->b(c);
into something roughly equivalent to: b(a, c);
-- a
then becomes the value of this
inside the function. When the function refers to data in the object, this
is dereferenced to find the correct object, then an offset is normally applied to find the correct item in that object.
If the member function never attempts to use any of the object's members, the fact that this
is a null pointer doesn't affect anything.
If, on the other hand, the member function does attempt to use a member of the object, it'll attempt to dereference this
to do that, and if this
is a NULL pointer, that won't work. Likewise, calling a virtual function always uses the vtable pointer, which is in the object, so attempting to call a virtual function via a null pointer can be expected to fail (regardless of whether the code in the virtual function refers to data in the object or not).
As I said to start with, I'm talking about the typical implementation here. From the viewpoint of the standard, you simply have undefined behavior, and that's the end of it. In theory, an implementation doesn't have to work the way I've described. In reality, however, essentially all the reasonably popular implementations of C++ (e.g., MS VC++, g++, Clang, and Intel) all work very similarly in these respects.