It works because the standard says that it works.
In practice, the compiler inserts implicit calls to ~A()
and ~B()
into ~AB()
. The mechanism is exactly the same as with single inheritance, except that there are multiple base destructors for the compiler to call.
I think the main source of confusion in your diagram is the multiple separate vtable entries for the virtual destructor. In practice, there will be a single entry that would point to ~A()
, ~B()
and ~AB()
for A
, B
and AB()
respectively.
For example, if I compile your code using gcc
and examine the assembly, I see the following code in ~AB()
:
LEHE0:
movq -24(%rbp), %rax
addq $16, %rax
movq %rax, %rdi
LEHB1:
call __ZN1BD2Ev
LEHE1:
movq -24(%rbp), %rax
movq %rax, %rdi
LEHB2:
call __ZN1AD2Ev
This calls ~B()
followed by ~A()
.
The virtual tables of the three classes look as follows:
; A
__ZTV1A:
.quad 0
.quad __ZTI1A
.quad __ZN1AD1Ev
.quad __ZN1AD0Ev
; B
__ZTV1B:
.quad 0
.quad __ZTI1B
.quad __ZN1BD1Ev
.quad __ZN1BD0Ev
; AB
__ZTV2AB:
.quad 0
.quad __ZTI2AB
.quad __ZN2ABD1Ev
.quad __ZN2ABD0Ev
.quad -16
.quad __ZTI2AB
.quad __ZThn16_N2ABD1Ev
.quad __ZThn16_N2ABD0Ev
For each class, entry #2 refers to the class's "complete object destructor". For A
, this points to ~A()
etc.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…