When a C object (subclass of B) is destroyed, compiler guarantees to clean up the entire real estate of the C object, which always always includes a B real estate. Therefore the B dtor is never bypassed. (Same deal with B constructor.)
Q: Now what if B has a ptr/ref to a heap object?
A: the heapy thingy is NOT in B real estate
A: Say the ptr field occupies 4 bytes of B real estate. The 4 bytes are reclaimed, but not the heap pointee object. You need to customize dtor to clean up heap.