Let's consider the following code:
#include <iostream>
struct A{ virtual void foo(){ } };
struct B : A { virtual void foo(){ } };
A *a = new B;
int main()
{
delete a; //UB?
}
I deliberately didn't define a virtual destructor. The compiler printed a message about causing UB, is it true?
Formally you have UB if you delete through a pointer to T, which is not the most derived type, and T doesn't have a virtual destructor.
In practice you can get away with it if you don't have any data mambers, but it's still very ungood and unnecessary practice.
Note: when you use a shared_ptr
it creates a deleter function at the point of initialization, and that deleter function can remember the original type, which, if that type is the most derived type, ensures well-defined deletion. E.g. in your case shared_ptr<A> p( new B );
would be OK.