C++对象模型-virtual继承
0.测试环境
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
1.虚继承
X
/ \
virtual / \virtual
/ \
A B
\ /
\ /
\ /
C
class X{
public:
int i;
virtual void show()
{
cout<<"X"<<endl;
}
virtual ~X()
{
cout<<"~X";
}
};
class A:public virtual X{
public:
int j;
virtual void show()
{
cout<<"A"<<endl;
}
virtual ~A()
{
cout<<"~A";
}
};
class B:public virtual X{
public:
int d;
virtual void show()
{
cout<<"B"<<endl;
}
virtual ~B()
{
cout<<"~B";
}
};
class C:public A, public B{
public:
int k;
virtual void show()
{
cout<<"C"<<endl;
}
virtual ~C()
{
cout<<"~C";
}
};
void test()
{
B *b= new C;
b->show();
delete b;
}
1.1 test()函数对应的汇编代码如下
_Z4testv: //test
.LFB1041:
.cfi_startproc
pushl %ebp //ebp压栈
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp //ebp=esp
.cfi_def_cfa_register 5
pushl %ebx //ebx压栈
subl $20, %esp //esp=esp-20
.cfi_offset 3, -12
subl $12, %esp
pushl $28 //malloc的内存大小, Class C Object的大小为28
call _Znwj //malloc内存
addl $16, %esp
movl %eax, %ebx //ebx=eax, eax为malloc的内存的首地址,即C.this
subl $12, %esp
pushl %ebx //C的首地址压栈
call _ZN1CC1Ev //C::C()
addl $16, %esp
testl %ebx, %ebx //ebx&ebx
je .L36 //if ebx&ebx=0, jmp
leal 8(%ebx), %eax //eax=C.this+8
/*
|-------|<-----C.this
| A |
|-------|<-----b
| B |
|-------|
| C |
|-------|
| X |
|-------|
*/
jmp .L37
.L36:
movl $0, %eax
.L37:
movl %eax, -12(%ebp) //b=C.this+8, 即b=BastTypeB.this, 对应的代码为b=new C;
movl -12(%ebp), %eax //eax=b
movl (%eax), %eax //eax=vptr
movl (%eax), %eax //eax=vptr[0]
subl $12, %esp
pushl -12(%ebp) //b的地址压栈
call *%eax //b->show()
addl $16, %esp
cmpl $0, -12(%ebp)
je .L39
movl -12(%ebp), %eax //eax=b
movl (%eax), %eax //eax=vptr
addl $8, %eax //eax=vptr+8
movl (%eax), %eax //eax=vptr[2], virtual机制
subl $12, %esp
pushl -12(%ebp) //b压栈
call *%eax //call vptr[2], delete b
addl $16, %esp
.L39:
//略
1.2 _ZN1CC1Ev对应的汇编代码如下:
_ZN1CC1Ev: //C:C()
.LFB1053:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $8, %esp
movl 8(%ebp), %eax //eax=this
addl $20, %eax //base type X的首地址
/*
|-------|<-----C.this
| A |
|-------|<-----b
| B |
|-------|
| C |
Offset 20 -->|-------|<-----eax
|vptr |
|-------|
*/
jmp .L37
subl $12, %esp
pushl %eax //压栈
call _ZN1XC2Ev //X:X()
/* _ZN1XC2Ev对应的代码如下:
_ZN1XC2Ev: //X::X()
.LFB1044:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl $_ZTV1X+8, %edx //dex=vptr4X
movl 8(%ebp), %eax //eax=this
movl %edx, (%eax) //[this]=vptr4X
nop
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
*/
addl $16, %esp
movl $_ZTT1C+4, %edx //edx=VTT+4
movl 8(%ebp), %eax //eax=this
subl $8, %esp
pushl %edx
pushl %eax
call _ZN1AC2Ev //A::A()
/*
_ZN1AC2Ev:
.LFB1047:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 12(%ebp), %eax //eax=VTT+4
movl (%eax), %edx //edx=VTT[1], edx=vptr
movl 8(%ebp), %eax //eax=this
movl %edx, (%eax) //[this]=vptr
movl 8(%ebp), %eax //eax=this
movl (%eax), %eax
subl $12, %eax //eax=vptr-12,即vtable+0获取offset to X
movl (%eax), %eax //eax=vtable[0], eax=20
movl %eax, %edx //edx=eax
movl 8(%ebp), %eax //eax=this
addl %eax, %edx //edx=this+offset2X, 即edx=this+20
movl 12(%ebp), %eax //eax=VTT+4
movl 4(%eax), %eax //eax=eax+4,即eax=VTT+8
movl %eax, (%edx) //[edx] =VTT+8,即设置X的vptr(construction vtable for A-in-C)
nop
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
*/
addl $16, %esp
movl $_ZTT1C+12, %edx //edx=VTT(VTT for C)+12
movl 8(%ebp), %eax //eax=this
addl $8, %eax //eax=eax+8,即base type B的地址
subl $8, %esp
pushl %edx //PUSH VTT+12, the address of vptr(_ZTC1C8_1B+12)
pushl %eax //PUSH B.this
call _ZN1BC2Ev //B::B()
/* 分析略
_ZN1BC2Ev:
.LFB1050:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 12(%ebp), %eax
movl (%eax), %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 8(%ebp), %eax
movl (%eax), %eax
subl $12, %eax
movl (%eax), %eax
movl %eax, %edx
movl 8(%ebp), %eax
addl %eax, %edx
movl 12(%ebp), %eax
movl 4(%eax), %eax
movl %eax, (%edx)
nop
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
*/
addl $16, %esp
movl $_ZTV1C+12, %edx //edx=VTT+12
movl 8(%ebp), %eax //eax=this
movl %edx, (%eax) //[this]=VTT+12
movl $20, %edx //edx=20
movl 8(%ebp), %eax //eax=this
addl %edx, %eax //eax=this+20
movl $_ZTV1C+64, %edx //edx=vptr4C
movl %edx, (%eax) //[this]=vptr4C
movl $_ZTV1C+36, %edx
movl 8(%ebp), %eax //eax=this
movl %edx, 8(%eax) //[this+8]=vptr4C
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
1.3 delete b对应的汇编代码如下:
大概意思就是:delete b,就是将base type class b的地址压栈,但是编译器生成的代码会做一层转换,自动将压栈的地址-8,即将C.this压栈,然后析构,然后free