There is no generically applicable way to guard against this; C/C++ do not have a generic pointer-validation facility.
As you've shown, you can fence against NULL pointers but not (easily) against invalid ones:
template <class T> bool isValid(T* ptr) { return ptr != NULL; }
class X {
int someVal;
int getSomeVal(void) const { return isValid<X>(this) ? someVal : -EINVAL; }
};
// the following will set 'j' to -EINVAL
X* nullX = (X*)NULL;
int j = nullX->getSomeVal();
// the following will make getSomeVal() crash:
X* invalptr = reinterpret_cast<X*>(0xdefeca7ed);
int i = invalptr->getSomeVal();
Some compilers / libraries / heap allocator classes (optionally) zero-initialize all heap memory and hence on such platforms, "uninitialized" heap contains zeroes - on such platforms the above is useful to guard against the use of uninitialized heap. But that costs performance and therefore isn't mandated by the C / C++ standards. And it doesn't help with uninitialized temporary objects (that are created on the stack not on the heap, and hence cannot be zero-filled by the heap allocator).
To guard against any stray pointer, you'd have to implement your own allocators, and provide hook calls (a much more elaborate version of isValid() above) allowing an object instance to query the allocator state regarding "hey allocator have you seen my this before ?", and make all class constructors for stack-based objects (de)register the object instance address with the "validity tracker" on creation/deletion. It'd be a very heavyweight approach to search the heap allocator maps each time any object method is called. I've not seen this in production practice, though some debugging tools (valgrind comes to mind) have allocator tracking, and some limited stray detection.
And if as a user of the library you desperately want to cheat, what stops you from doing:
X* objX = new X();
Y* objY = reinterpret_cast<Y*>(objX);
Y->someMethodThatAccessesThingsWayOutsideTheSizeOf_X();
Even if that method would validate this with the allocator, it'd find it ok/known but how would you tell that it "really is an Y" there ?
In general, a library's resilience depends, to a large degree, on the user. How much nannying is too much ?