大家都说虚表虚指针,有虚表虚指针,怎么就能动态的指向虚表呢?

用Compiler Explorer一下就看出来了。这个代码片直接用CE还带颜色,更方便

#include <cstdio>
class base{
public:
  virtual void p(){printf("base\n");}
  virtual void q(){}
  virtual void r(){}
  virtual ~base(){}
};

class derive : public base{
  public:
  virtual void p(){printf("derive\n");}
};

void f(base*b){
	b->p();
}

int main()
{
  base* b=new base;
  base* d=new derive;
  f(b);
  f(d);
}

汇编是这样的

base::q():
        ret
base::r():
        ret
base::~base() [base object destructor]:
        ret
derive::~derive() [base object destructor]:
        ret
.LC0:
        .string "derive"
derive::p():
        mov     edi, OFFSET FLAT:.LC0
        jmp     puts
.LC1:
        .string "base"
base::p():
        mov     edi, OFFSET FLAT:.LC1
        jmp     puts
base::~base() [deleting destructor]:
        mov     esi, 8
        jmp     operator delete(void*, unsigned long)
derive::~derive() [deleting destructor]:
        mov     esi, 8
        jmp     operator delete(void*, unsigned long)
f(base*):
        mov     rax, QWORD PTR [rdi]
        jmp     [QWORD PTR [rax]]
main:
        push    r12
        mov     edi, 8
        push    rbp
        sub     rsp, 8
        call    operator new(unsigned long)
        mov     edi, 8
        mov     QWORD PTR [rax], OFFSET FLAT:vtable for base+16
        mov     r12, rax
        call    operator new(unsigned long)
        mov     rdi, r12
        mov     rbp, rax
        mov     QWORD PTR [rax], OFFSET FLAT:vtable for derive+16
        mov     rax, QWORD PTR [r12]
        call    [QWORD PTR [rax]]
        mov     rax, QWORD PTR [rbp+0]
        mov     rdi, rbp
        call    [QWORD PTR [rax]]
        add     rsp, 8
        xor     eax, eax
        pop     rbp
        pop     r12
        ret
typeinfo name for base:
        .string "4base"
typeinfo for base:
        .quad   vtable for __cxxabiv1::__class_type_info+16
        .quad   typeinfo name for base
typeinfo name for derive:
        .string "6derive"
typeinfo for derive:
        .quad   vtable for __cxxabiv1::__si_class_type_info+16
        .quad   typeinfo name for derive
        .quad   typeinfo for base
vtable for base:
        .quad   0
        .quad   typeinfo for base
        .quad   base::p()
        .quad   base::q()
        .quad   base::r()
        .quad   base::~base() [complete object destructor]
        .quad   base::~base() [deleting destructor]
vtable for derive:
        .quad   0
        .quad   typeinfo for derive
        .quad   derive::p()
        .quad   base::q()
        .quad   base::r()
        .quad   derive::~derive() [complete object destructor]
        .quad   derive::~derive() [deleting destructor]

注意两次调用new构造后面,有一个保存指针动作

        mov     QWORD PTR [rax], OFFSET FLAT:vtable for base+16

这就是把p函数指针保存起来,vtable+偏移,然后保存到

不理解的地方就是这里了,所谓的动态实际上在代码中也是死的,主要是在构造后多了个存偏移指针,需要额外的开销。


参考