The standard clearly states that such deletion causes undefined behavior. No reservations are made on whether any fields are added to the heir class or not.
5.3.5 Delete
It has been noted that the have a virtual destructor or the behavior is undefined. [...]
On the destructor of the class, some implicit utility functions are also usually superimposed (may overlap), which can work incorrectly when the wrong destructor is called.
In particular, in a typical implementation, the destructor is charged with calling the correct function operator delete , which can be overloaded for each class separately. That is, if the static function operator delete were defined in struct B , then without a virtual destructor, it would have to be provided with some other methods to provide its functionality. (The authors of the standard, most likely, were guided by just such an implementation of a call to an overloaded operator delete .)
for example
#include <cstdlib> struct A { // virtual ~A() {} }; struct B : A { static void *operator new(size_t s) { return (char *) std::malloc(s * 2) + s; } static void operator delete(void *p, size_t s) { std::free((char *) p - s); } }; int main() { A* p = new B; delete p; }
In popular implementations, such code will fall due to an invalid operator delete called. But it is enough to add a virtual destructor to A , and everything will be fine. Note that both members of B are static functions, that is, nothing is physically added to B