Undefined behavior is what makes C efficient. And NULL pointers are just a consequence of the ability of pointers to take numerical values. Naturally, this behavior was kept in C++, which is just a superset of C (with a few minor exceptions).
NULL pointers and undefined behavior make a lot of sense in C++. The mistake is replicating this behavior on languages where it doesn't.
> And NULL pointers are just a consequence of the ability of pointers to take numerical values.
Not at all. The C standard doesn't allow pointers to take arbitrary numerical values; while you can cast back and forth, the only guarantee is that casting a pointer to a numeric value and back to a pointer still points to the original object (and even then only if the numeric type used is large enough, which couldn't be portably ensured prior to C99, and is only conditionally supported even now).
In particular, one thing that the standard does not guarantee - and there have been implementations that did not do so - is that casting a null pointer to int will produce zero, or that casting int zero to a pointer will produce null. Nor does it guarantee that a null pointer is an all-bits-zero value.
The fact that you can write "p = 0" in C is not because pointers can take arbitrary numerical values, but because the language syntax and semantics allow you to do so, with 0 being treated specially when assigned to an lvalue of a pointer type. But you can't write "p = 1", for example, because there's no such special treatment for any other integer literal.
NULL pointers and undefined behavior make a lot of sense in C++. The mistake is replicating this behavior on languages where it doesn't.