0.测试环境
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
1, 默认构造函数在编译器需要的时产生出来
在如下片段的代码中, 通过分析汇编代码,发现并不会合成出来一个Default Constructor函数,因为如下代码是代码逻辑需要一个默认构造函数来初始化val和pnext数据成员。而不是编译器需要合成一个Default Constructor.
代码语言:javascript复制#include<iostream>
using namespace std;
class Foo{
public:
int val;
Foo *pnext;
};
void foobar()
{
Foo bar;
if(bar.val || bar.pnext) //代码逻辑需要一个Default Constructor来初始化val和pnext数据成员
{
cout<<bar.val<<endl;
}
}
以上代码对应的汇编代码如下(g -S -m32 p39.cc):
代码语言:javascript复制_Z6foobarv: //foobar(), c filt _Z6foobarv
.LFB1021:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $24, %esp
movl -16(�p), �x //eax=[ebp-16],即eax = bar.val ,NOTICE:NO Default Constructor is Called
testl �x, �x //eax&eax
jne .L2 // 如果testl的结果非0,jmp .L2 ,jne means jmp if not equal
movl -12(�p), �x //eax=[ebp-12], 即eax=bra.pnext
testl �x, �x
je .L4 //如果testl的结果为0, jmp .L4
.L2:
movl -16(�p), �x //eax=[ebp-16], 即bar.val
subl $8, %esp
pushl �x
pushl $_ZSt4cout
call _ZNSolsEi
addl $16, %esp
subl $8, %esp
pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
pushl �x
call _ZNSolsEPFRSoS_E
addl $16, %esp
.L4:
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
2. nontrivial Default Constructor
nontrivial Default Constrctor 即编译器所需要的默认构造函数,必要时候会由编译器合成出一个。
2.1 “带有Default Constructor”的Member Class Object
代码语言:javascript复制#include<iostream>
using namespace std;
class Foo{
public:
Foo():_f(0)
{
}
private:
int _f;
};
class Bar{
public:
char *str;
Foo foo;
/*
NOTICE:即使写了默认构造函数,Bar的数据成员的初始化顺序也是:按数据成员的声明顺序初始化,即
先初始化str,然后调用foo的默认构造函数
Bar():foo(), str(NULL)
{
}
*/
};
void test()
{
Bar bar;
if(bar.str)
{
cout<<bar.str<<endl;
}
}
test函数对应的汇编代码如下:
代码语言:javascript复制_Z4testv:
.LFB1024:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $24, %esp
movl %gs:20, �x
movl �x, -12(�p)
xorl �x, �x
subl $12, %esp
leal -20(�p), �x //eax=bar的首地址
pushl �x //bar对象首地址压栈
call _ZN3BarC1Ev //Bar::Bar()
addl $16, %esp
movl -20(�p), �x
testl �x, �x
je .L6
movl -20(�p), �x
subl $8, %esp
pushl �x
pushl $_ZSt4cout
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
addl $16, %esp
subl $8, %esp
pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
pushl �x
call _ZNSolsEPFRSoS_E
addl $16, %esp
.L6:
nop
movl -12(�p), �x
xorl %gs:20, �x
je .L5
call __stack_chk_fail
.L5:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
Bar构造函数对应的汇编代码如下:
代码语言:javascript复制_ZN3BarC2Ev: //Bar::Bar()
.LFB1026:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $8, %esp
movl 8(�p), �x //eax=this
addl $4, �x //eax=eax 4,即foo的首地址
subl $12, %esp
pushl �x //foo对象的this指针压栈
call _ZN3FooC1Ev //Foo:Foo(), NOTICE:初始化Bar::foo时编译器的责任,初始化Bar::str时代码逻辑的责任
addl $16, %esp
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
Bar::Bar()函数伪码如下:
代码语言:javascript复制Bar的default construtor合成如下:
Bar::Bar()
{
Foo::Foo(&foo);
}
通过分析以上代码,发现: 如果一个class没有任何Constructor,但他的数据成员含有member object(该object有默认构造函数),那么 该class的implicit default constructor 就是nontrivial的,编译器会为该class合成一个default contrutor.
3.”带有Default Construtor“的Base Class
代码语言:javascript复制nclude<iostream>
using namespace std;
class Foo{
public:
Foo():_f(0)
{
}
private:
int _f;
};
class Bar :public Foo{
public:
char *str;
Foo foo;
};
void test()
{
Bar bar;
if(bar.str)
{
cout<<bar.str<<endl;
}
}
对应的汇编代码如下(g -S -m32 p44.cc):
代码语言:javascript复制_ZN3BarC2Ev: //Bar::Bar()
.LFB1026:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $8, %esp
movl 8(�p), �x //eax=[ebp 8], eax=this
subl $12, %esp
pushl �x //this指针压栈
call _ZN3FooC2Ev //Foo:Foo()
addl $16, %esp
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1026:
.size _ZN3BarC2Ev, .-_ZN3BarC2Ev
.weak _ZN3BarC1Ev
.set _ZN3BarC1Ev,_ZN3BarC2Ev
.text
.globl _Z4testv
.type _Z4testv, @function
_Z4testv:
.LFB1024:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $24, %esp
movl %gs:20, �x
movl �x, -12(�p)
xorl �x, �x
subl $12, %esp
leal -20(�p), �x //eax=bar的首地址
pushl �x //bar.this压栈
call _ZN3BarC1Ev //call Bar::Bar()
addl $16, %esp
movl -16(�p), �x
testl �x, �x
je .L6
movl -16(�p), �x
subl $8, %esp
pushl �x
pushl $_ZSt4cout
//略
Bar::Bar()的伪码如下:
代码语言:javascript复制Bar::Bar(&bar)
{
Foo:Foo(&bar):
}
通过分析代码,发现:如果一个没有任何construtor的class继承了一个带有default construtor的base class, 那么该derived class的cdefualt constrtor会被编译器合成出来,该defaulut constructor为nontrivial
4. “带有virtual function”的class
代码语言:javascript复制#include<iostream>
using namespace std;
class Widget{
public:
virtual void show()=0;
int _w;
};
class Bell:public Widget{
public:
void show()
{
cout<<"Bell Bell..."<<endl;
}
};
void test()
{
Bell b;
Widget &w =b;
w.show();
}
对应的汇编代码如下:
代码语言:javascript复制_ZN6WidgetC2Ev: //Widget::Widget()
.LFB1025:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
movl $_ZTV6Widget 8, �x //edx = vptr
movl 8(�p), �x //eax=this
movl �x, (�x) //[this]=vptr
nop
popl �p
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
_ZN4BellC2Ev: //Bell:Bell()
.LFB1027:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $8, %esp
movl 8(�p), �x //eax=this
subl $12, %esp //esp=esp-12
pushl �x //this压栈
call _ZN6WidgetC2Ev //调用base class的默认构造函数, Widget:Widget()
addl $16, %esp
movl $_ZTV4Bell 8, �x //edx=vptr4Bell
movl 8(�p), �x //eax=this
movl �x, (�x) //[this]=vptr4Bell
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
_Z4testv:
.LFB1022:
.cfi_startproc
pushl �p
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p
.cfi_def_cfa_register 5
subl $24, %esp
movl %gs:20, �x
movl �x, -12(�p)
xorl �x, �x
subl $12, %esp
leal -20(�p), �x //eax=ebp-20,即eax=b.this
pushl �x //b首地址压栈
call _ZN4BellC1Ev //Bell::Bell()
addl $16, %esp
leal -20(�p), �x //eax=b.this
movl �x, -24(�p) //Widget &w=b
movl -24(�p), �x //eax=&w
movl (�x), �x //eax=vptr4Bell
movl (�x), �x //eax = vptr4Bell[0]
subl $12, %esp
pushl -24(�p) //w.this压栈
call *�x //virtual机制,w.show()
addl $16, %esp
nop
movl -12(�p), �x
xorl %gs:20, �x
je .L5
call __stack_chk_fail
.L5:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc