Во время выполнения разрешаются только вызовы виртуальных функций. Если функции не виртуальные, как в вашем примере, вызовы разрешаются во время компиляции.
При просмотре неоптимизированной сборки (сгенерированной gcc 9.2 с -O0), основная функция компилируется в:
main:
push rbp
mov rbp, rsp
sub rsp, 16
lea rax, [rbp-1]
mov rdi, rax
call _ZN1A4dispEv
lea rax, [rbp-2]
mov rdi, rax
call _ZN1B4dispEv
mov eax, 0
leave
ret
Странные имена _ZN1A4dispEv
и _ZN1B4dispEv
являются результатом искажения имен: в подобных случаях компилятор создает уникальные имена для функций (имейте в виду, что в ассемблере нет опций). Фактическое искажение определяется компилятором, но вы можете заметить имя класса (A
и B
соответственно) и имя функции disp
.
Если disp
были объявлены какvirtual
в A
, тогда main
будет выглядеть следующим образом:
main:
push rbp
mov rbp, rsp
sub rsp, 16
-> mov eax, OFFSET FLAT:vtable for A+16
-> mov QWORD PTR [rbp-8], rax
lea rax, [rbp-8]
mov rdi, rax
call A::disp()
-> mov eax, OFFSET FLAT:vtable for B+16
-> mov QWORD PTR [rbp-16], rax
lea rax, [rbp-16]
mov rdi, rax
call B::disp()
mov eax, 0
leave
ret
Строки, отмеченные ->
, представляют собой запросы vtable, используемые для разрешения вызова функции во время выполнения.