Деструкторы вызываются по порядку, как если бы они раскручивали эффекты соответствующих конструкторов. Итак, сначала деструкторы производных объектов, затем деструкторы базовых объектов. А виртуализация деструкторов не влияет на вызов / не вызов деструктора базовый класс .
Также стоит упомянуть, что ваш пример можно упростить таким образом (этот код также приводит к двойному вызову базового деструктора и однократного получения деструктора) :
struct A {
~A() {
// ...
}
};
struct B: A {
~B() {
// ...
}
};
int main() {
A a;
B b;
}