Say, the code
class Derived: public Base {....}
Base* b_ptr = new( malloc(sizeof(Derived)) ) Base(1);
b_ptr->f(2);
Derived* d_ptr = new(b
If you write this code more cleanly, it is easier to see what goes wrong:
void * addr = std::malloc(LARGE_NUMBER);
Base * b = new (addr) Base;
b->foo(); // no problem
Derived * d = new (addr) Derived;
d->bar(); // also fine (#1)
b->foo(); // Error! b no longer points to a Base!
static_cast (d)->foo(); // OK
b = d; b->foo(); // also OK
The problem is that at the line marked (#1), b and d point to entirely separate, unrelated things, and since you overwrote the memory of the erstwhile object *b, b is in fact no longer valid at all.
You may have some misguided thoughts about Base* and Derived* being convertible pointer types, but that has nothing to do with the present situation, and for the sake of this example, the two types may as well be entirely unrelated. It is only one the very last two lines that we use the fact that the Derived* is convertible to a Base*, when we perform the actual conversion. But note that this conversion is a genuine value conversion, and d is not the same pointer as static_cast (at least as far as the language is concerned).
Finally, let's clean up this mess:
d->~Derived();
std::free(addr);
The opportunity to destroy the original *b has passed, so we may have leaked that.