#include <stdio.h>
class Base{
public:
Base(){
puts("Base()");
}
virtual void fun1()
{
puts("Base::fun1()");
}
virtual void fun2()
{
puts("Base::fun2()");
}
virtual void fun3()
{
puts("Base::fun3()");
}
void fun4(){
printf("Normal Base::fun4()\n");
}
~Base(){
puts("~Base()");
}
};
class Derive : public Base
{
public:
Derive()
{
puts("Derive()");
}
void fun1()
{
puts("Derive::fun1()");
}
void fun2()
{
puts("Derive::fun2()");
}
/* void fun3()
{
puts("Derive::fun3()");
}
*/
void fun4(){
printf("Normal Derive::fun4()\n");
}
~Derive(){
puts("~Derive()");
}
};
typedef void (*FUN) (void);
int main()
{
FUN pfun1 = NULL;
FUN pfun2 = NULL;
FUN pfun3 = NULL;
Derive b;
printf("&b = %p\n",(&b));
printf("vtable = %p\n",(int *)(&b));
printf("*vtable = %p\n",**(int **)(&b));//取vtable第一个函数的地址
printf("b = %p\n",*(int*)(&b));
pfun1 = (FUN)**(int**)(&b);
printf("fun1 = %p\n",pfun1);
pfun1();
pfun2 = (FUN)*(*(int**)(&b)+1);
printf("fun2 = %p\n",pfun2);
pfun2();
//b.fun2();
pfun3 = (FUN)*(*(int**)(&b)+2);
printf("fun3 = %p\n",pfun3);
pfun3();
b.fun4();
Base * pb = &b;
pb->fun2();
pb->fun3();
pb->fun4();
return 0;
}
/* 输出结果如下
Base()
Derive()
&b = 0xbfe919dc
vtable = 0xbfe919dc
*vtable = 0x80487ca
b = 0x80489e8
fun1 = 0x80487ca
Derive::fun1()
fun2 = 0x80487de
Derive::fun2()
fun3 = 0x804874c
Base::fun3()
Normal Derive::fun4()
Derive::fun2()
Base::fun3()
Normal Base::fun4()
~Derive()
~Base()
*/
//看一下汇编代码是如何实现的
/*
.file "vtable.cpp"
.section .rodata
.LC0:
.string "Base()"
.section .text._ZN4BaseC2Ev,"axG",@progbits,_ZN4BaseC2Ev,comdat
.align 2
.weak _ZN4BaseC2Ev
.type _ZN4BaseC2Ev, @function
_ZN4BaseC2Ev: #Base的构造函数
.LFB1:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl 8(%ebp), %eax
movl $_ZTV4Base+8, (%eax) #Base的vtable赋值给对象的前4个字节的地址空间
movl $.LC0, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size _ZN4BaseC2Ev, .-_ZN4BaseC2Ev
.section .rodata
.LC1:
.string "Base::fun1()"
.section .text._ZN4Base4fun1Ev,"axG",@progbits,_ZN4Base4fun1Ev,comdat
.align 2
.weak _ZN4Base4fun1Ev
.type _ZN4Base4fun1Ev, @function
_ZN4Base4fun1Ev:
.LFB3:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC1, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE3:
.size _ZN4Base4fun1Ev, .-_ZN4Base4fun1Ev
.section .rodata
.LC2:
.string "Base::fun2()"
.section .text._ZN4Base4fun2Ev,"axG",@progbits,_ZN4Base4fun2Ev,comdat
.align 2
.weak _ZN4Base4fun2Ev
.type _ZN4Base4fun2Ev, @function
_ZN4Base4fun2Ev:
.LFB4:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC2, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE4:
.size _ZN4Base4fun2Ev, .-_ZN4Base4fun2Ev
.section .rodata
.LC3:
.string "Base::fun3()"
.section .text._ZN4Base4fun3Ev,"axG",@progbits,_ZN4Base4fun3Ev,comdat
.align 2
.weak _ZN4Base4fun3Ev
.type _ZN4Base4fun3Ev, @function
_ZN4Base4fun3Ev:
.LFB5:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC3, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE5:
.size _ZN4Base4fun3Ev, .-_ZN4Base4fun3Ev
.section .rodata
.LC4:
.string "Normal Base::fun4()"
.section .text._ZN4Base4fun4Ev,"axG",@progbits,_ZN4Base4fun4Ev,comdat
.align 2
.weak _ZN4Base4fun4Ev
.type _ZN4Base4fun4Ev, @function
_ZN4Base4fun4Ev:
.LFB6:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC4, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE6:
.size _ZN4Base4fun4Ev, .-_ZN4Base4fun4Ev
.section .rodata
.LC5:
.string "~Base()"
.section .text._ZN4BaseD2Ev,"axG",@progbits,_ZN4BaseD2Ev,comdat
.align 2
.weak _ZN4BaseD2Ev
.type _ZN4BaseD2Ev, @function
_ZN4BaseD2Ev:
.LFB8:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl 8(%ebp), %eax
movl $_ZTV4Base+8, (%eax)
movl $.LC5, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE8:
.size _ZN4BaseD2Ev, .-_ZN4BaseD2Ev
.section .rodata
.LC6:
.string "Derive()"
.globl _Unwind_Resume
.section .text._ZN6DeriveC1Ev,"axG",@progbits,_ZN6DeriveC1Ev,comdat
.align 2
.weak _ZN6DeriveC1Ev
.type _ZN6DeriveC1Ev, @function
_ZN6DeriveC1Ev: #Derive的构造函数
.LFB12:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
.cfi_lsda 0x0,.LLSDA12
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
subl $16, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
.LEHB0:
.cfi_offset 3, -16
.cfi_offset 6, -12
call _ZN4BaseC2Ev #先调用基类Base的构造函数 得到基类的vtable
.LEHE0:
movl 8(%ebp), %eax
movl $_ZTV6Derive+8, (%eax) #用子类的vtable覆盖
movl $.LC6, (%esp)
.LEHB1:
call puts
.LEHE1:
jmp .L18
.L17:
.L15:
movl %edx, %ebx
movl %eax, %esi
movl 8(%ebp), %eax
movl %eax, (%esp)
call _ZN4BaseD2Ev
movl %esi, %eax
movl %ebx, %edx
movl %eax, (%esp)
.LEHB2:
call _Unwind_Resume
.LEHE2:
.L18:
addl $16, %esp
popl %ebx
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE12:
.size _ZN6DeriveC1Ev, .-_ZN6DeriveC1Ev
.globl __gxx_personality_v0
.section .gcc_except_table,"a",@progbits
.LLSDA12:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE12-.LLSDACSB12
.LLSDACSB12:
.uleb128 .LEHB0-.LFB12
.uleb128 .LEHE0-.LEHB0
.uleb128 0x0
.uleb128 0x0
.uleb128 .LEHB1-.LFB12
.uleb128 .LEHE1-.LEHB1
.uleb128 .L17-.LFB12
.uleb128 0x0
.uleb128 .LEHB2-.LFB12
.uleb128 .LEHE2-.LEHB2
.uleb128 0x0
.uleb128 0x0
.LLSDACSE12:
.section .text._ZN6DeriveC1Ev,"axG",@progbits,_ZN6DeriveC1Ev,comdat
.section .rodata
.LC7:
.string "Derive::fun1()"
.section .text._ZN6Derive4fun1Ev,"axG",@progbits,_ZN6Derive4fun1Ev,comdat
.align 2
.weak _ZN6Derive4fun1Ev
.type _ZN6Derive4fun1Ev, @function
_ZN6Derive4fun1Ev:
.LFB13:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC7, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE13:
.size _ZN6Derive4fun1Ev, .-_ZN6Derive4fun1Ev
.section .rodata
.LC8:
.string "Derive::fun2()"
.section .text._ZN6Derive4fun2Ev,"axG",@progbits,_ZN6Derive4fun2Ev,comdat
.align 2
.weak _ZN6Derive4fun2Ev
.type _ZN6Derive4fun2Ev, @function
_ZN6Derive4fun2Ev:
.LFB14:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC8, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE14:
.size _ZN6Derive4fun2Ev, .-_ZN6Derive4fun2Ev
.section .rodata
.LC9:
.string "Normal Derive::fun4()"
.section .text._ZN6Derive4fun4Ev,"axG",@progbits,_ZN6Derive4fun4Ev,comdat
.align 2
.weak _ZN6Derive4fun4Ev
.type _ZN6Derive4fun4Ev, @function
_ZN6Derive4fun4Ev:
.LFB15:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $.LC9, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE15:
.size _ZN6Derive4fun4Ev, .-_ZN6Derive4fun4Ev
.section .rodata
.LC10:
.string "~Derive()"
.section .text._ZN6DeriveD1Ev,"axG",@progbits,_ZN6DeriveD1Ev,comdat
.align 2
.weak _ZN6DeriveD1Ev
.type _ZN6DeriveD1Ev, @function
_ZN6DeriveD1Ev:
.LFB18:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
.cfi_lsda 0x0,.LLSDA18
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
subl $16, %esp
movl 8(%ebp), %eax
movl $_ZTV6Derive+8, (%eax)
movl $.LC10, (%esp)
.LEHB3:
.cfi_offset 3, -16
.cfi_offset 6, -12
call puts
.LEHE3:
movl 8(%ebp), %eax
movl %eax, (%esp)
.LEHB4:
call _ZN4BaseD2Ev
.LEHE4:
addl $16, %esp
popl %ebx
.cfi_remember_state
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.L29:
.cfi_restore_state
.L27:
movl %edx, %ebx
movl %eax, %esi
movl 8(%ebp), %eax
movl %eax, (%esp)
call _ZN4BaseD2Ev
movl %esi, %eax
movl %ebx, %edx
movl %eax, (%esp)
.LEHB5:
call _Unwind_Resume
.LEHE5:
.cfi_endproc
.LFE18:
.size _ZN6DeriveD1Ev, .-_ZN6DeriveD1Ev
.section .gcc_except_table
.LLSDA18:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE18-.LLSDACSB18
.LLSDACSB18:
.uleb128 .LEHB3-.LFB18
.uleb128 .LEHE3-.LEHB3
.uleb128 .L29-.LFB18
.uleb128 0x0
.uleb128 .LEHB4-.LFB18
.uleb128 .LEHE4-.LEHB4
.uleb128 0x0
.uleb128 0x0
.uleb128 .LEHB5-.LFB18
.uleb128 .LEHE5-.LEHB5
.uleb128 0x0
.uleb128 0x0
.LLSDACSE18:
.section .text._ZN6DeriveD1Ev,"axG",@progbits,_ZN6DeriveD1Ev,comdat
.section .rodata
.LC11:
.string "&b = %p\n"
.LC12:
.string "vtable = %p\n"
.LC13:
.string "*vtable = %p\n"
.LC14:
.string "b = %p\n"
.LC15:
.string "fun1 = %p\n"
.LC16:
.string "fun2 = %p\n"
.LC17:
.string "fun3 = %p\n"
.text
.globl main
.type main, @function
main:
.LFB19:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
.cfi_lsda 0x0,.LLSDA19
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
andl $-16, %esp
pushl %esi
pushl %ebx
subl $56, %esp
movl $0, 32(%esp)
movl $0, 36(%esp)
movl $0, 40(%esp)
leal 28(%esp), %eax
movl %eax, (%esp)
.LEHB6:
.cfi_escape 0x10,0x3,0x8,0x75,0x0,0x9,0xf0,0x1a,0x9,0xf8,0x22
.cfi_escape 0x10,0x6,0x8,0x75,0x0,0x9,0xf0,0x1a,0x9,0xfc,0x22
call _ZN6DeriveC1Ev #调用构造函数
.LEHE6:
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $.LC11, (%esp)
.LEHB7:
call printf #printf("&b = %p\n",(&b));
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $.LC12, (%esp)
call printf #printf("vtable = %p\n",(int *)(&b));
leal 28(%esp), %eax
movl (%eax), %eax
movl (%eax), %eax
movl %eax, 4(%esp)
movl $.LC13, (%esp)
call printf #printf("*vtable = %p\n",**(int **)(&b));
leal 28(%esp), %eax
movl (%eax), %eax
movl %eax, 4(%esp)
movl $.LC14, (%esp)
call printf #printf("b = %p\n",*(int*)(&b));
leal 28(%esp), %eax
movl (%eax), %eax
movl (%eax), %eax
movl %eax, 32(%esp)
movl 32(%esp), %eax
movl %eax, 4(%esp)
movl $.LC15, (%esp)
call printf #printf("fun1 = %p\n",pfun1);
movl 32(%esp), %eax
call *%eax # pfun1();
leal 28(%esp), %eax
movl (%eax), %eax
addl $4, %eax
movl (%eax), %eax
movl %eax, 36(%esp)
movl 36(%esp), %eax
movl %eax, 4(%esp)
movl $.LC16, (%esp)
call printf #printf("fun2 = %p\n",pfun2);
movl 36(%esp), %eax
call *%eax #pfun2();
leal 28(%esp), %eax
movl (%eax), %eax
addl $8, %eax
movl (%eax), %eax
movl %eax, 40(%esp)
movl 40(%esp), %eax
movl %eax, 4(%esp)
movl $.LC17, (%esp)
call printf # printf("fun3 = %p\n",pfun3);
movl 40(%esp), %eax
call *%eax #pfun3();
leal 28(%esp), %eax
movl %eax, (%esp)
call _ZN6Derive4fun4Ev # b.fun4();
leal 28(%esp), %eax #基类指针指向子类对象
movl %eax, 44(%esp)
movl 44(%esp), %eax
movl (%eax), %eax #经过一系列变幻得到*vtable
addl $4, %eax #加4 得到_ZN6Derive4fun2Ev 即fun2。虽然都是fun2 基类是_ZN4Base4fun2Ev 与子类是不同的
movl (%eax), %edx
movl 44(%esp), %eax
movl %eax, (%esp)
call *%edx #pb->fun2();
movl 44(%esp), %eax
movl (%eax), %eax
addl $8, %eax #加8_ZN4Base4fun3Ev,因为子类没有重写所以这是基类Base的函数原型
movl (%eax), %edx
movl 44(%esp), %eax
movl %eax, (%esp)
call *%edx #pb->fun3();
movl 44(%esp), %eax
movl %eax, (%esp)
call _ZN4Base4fun4Ev # pb->fun4();;
.LEHE7:
movl $0, %ebx
leal 28(%esp), %eax
movl %eax, (%esp)
.LEHB8:
call _ZN6DeriveD1Ev
.LEHE8:
movl %ebx, %eax
addl $56, %esp
popl %ebx
.cfi_remember_state
.cfi_restore 3
popl %esi
.cfi_restore 6
movl %ebp, %esp
.cfi_def_cfa_register 4
popl %ebp
.cfi_restore 5
.cfi_def_cfa_offset 4
ret
.L33:
.cfi_restore_state
.L31:
movl %edx, %ebx
movl %eax, %esi
leal 28(%esp), %eax
movl %eax, (%esp)
call _ZN6DeriveD1Ev
movl %esi, %eax
movl %ebx, %edx
movl %eax, (%esp)
.LEHB9:
call _Unwind_Resume
.LEHE9:
.cfi_endproc
.LFE19:
.size main, .-main
.section .gcc_except_table
.LLSDA19:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE19-.LLSDACSB19
.LLSDACSB19:
.uleb128 .LEHB6-.LFB19
.uleb128 .LEHE6-.LEHB6
.uleb128 0x0
.uleb128 0x0
.uleb128 .LEHB7-.LFB19
.uleb128 .LEHE7-.LEHB7
.uleb128 .L33-.LFB19
.uleb128 0x0
.uleb128 .LEHB8-.LFB19
.uleb128 .LEHE8-.LEHB8
.uleb128 0x0
.uleb128 0x0
.uleb128 .LEHB9-.LFB19
.uleb128 .LEHE9-.LEHB9
.uleb128 0x0
.uleb128 0x0
.LLSDACSE19:
.text
.weak _ZTV6Derive
.section .rodata._ZTV6Derive,"aG",@progbits,_ZTV6Derive,comdat
.align 8
.type _ZTV6Derive, @object
.size _ZTV6Derive, 20
_ZTV6Derive:
.long 0
.long _ZTI6Derive
.long _ZN6Derive4fun1Ev #Derive vtable
.long _ZN6Derive4fun2Ev
.long _ZN4Base4fun3Ev
.weak _ZTV4Base
.section .rodata._ZTV4Base,"aG",@progbits,_ZTV4Base,comdat
.align 8
.type _ZTV4Base, @object
.size _ZTV4Base, 20
_ZTV4Base:
.long 0
.long _ZTI4Base
.long _ZN4Base4fun1Ev #Base vtable
.long _ZN4Base4fun2Ev
.long _ZN4Base4fun3Ev
.weak _ZTS6Derive
.section .rodata._ZTS6Derive,"aG",@progbits,_ZTS6Derive,comdat
.type _ZTS6Derive, @object
.size _ZTS6Derive, 8
_ZTS6Derive:
.string "6Derive"
.weak _ZTI6Derive
.section .rodata._ZTI6Derive,"aG",@progbits,_ZTI6Derive,comdat
.align 4
.type _ZTI6Derive, @object
.size _ZTI6Derive, 12
_ZTI6Derive:
.long _ZTVN10__cxxabiv120__si_class_type_infoE+8
.long _ZTS6Derive
.long _ZTI4Base
.weak _ZTS4Base
.section .rodata._ZTS4Base,"aG",@progbits,_ZTS4Base,comdat
.type _ZTS4Base, @object
.size _ZTS4Base, 6
_ZTS4Base:
.string "4Base"
.weak _ZTI4Base
.section .rodata._ZTI4Base,"aG",@progbits,_ZTI4Base,comdat
.align 4
.type _ZTI4Base, @object
.size _ZTI4Base, 8
_ZTI4Base:
.long _ZTVN10__cxxabiv117__class_type_infoE+8
.long _ZTS4Base
.ident "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"
.section .note.GNU-stack,"",@progbits
可以看出每个类都会通过继承链准备好自己的虚函数列表,所谓的基类指针指向子类对象的多态特性,是通过保存在子类对象的虚函数表实现的
而非虚函数的调用,如最后的 pb->fun4(); 无法通过pb保存的对象的地址空间的信息确定调用哪个,所以只能根据pb的类型来确定 如果这样调用 ((Derive*)pb)->fun4();
则会输出Normal Derive::fun4(),调用Derive::fun4()。
由此可知普通函数调用编译器根据指针类型来确定,虚函数调用编译器根据对应类的虚函数表确定。
*/
编译器是如何用汇编语言实现C++的虚函数表和隐式传递this指针(二)
最新推荐文章于 2022-08-07 17:43:22 发布