前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++对象模型-Default Constructor

C++对象模型-Default Constructor

作者头像
chinchao.xyz
发布2022-04-25 15:14:16
6160
发布2022-04-25 15:14:16
举报
文章被收录于专栏:k8s快速入门k8s快速入门
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   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        subl    $24, %esp
        movl    -16(%ebp), %eax  //eax=[ebp-16],即eax = bar.val  ,NOTICE:NO Default Constructor is Called
        testl   %eax, %eax //eax&eax
        jne     .L2 // 如果testl的结果非0,jmp .L2 ,jne means jmp if not equal
        movl    -12(%ebp), %eax //eax=[ebp-12], 即eax=bra.pnext
        testl   %eax, %eax
        je      .L4  //如果testl的结果为0, jmp .L4
.L2:
        movl    -16(%ebp), %eax  //eax=[ebp-16], 即bar.val
        subl    $8, %esp
        pushl   %eax
        pushl   $_ZSt4cout
        call    _ZNSolsEi
        addl    $16, %esp
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        pushl   %eax
        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   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        subl    $24, %esp
        movl    %gs:20, %eax
        movl    %eax, -12(%ebp)
        xorl    %eax, %eax
        subl    $12, %esp
        leal    -20(%ebp), %eax  //eax=bar的首地址
        pushl   %eax   //bar对象首地址压栈
        call    _ZN3BarC1Ev  //Bar::Bar()
        addl    $16, %esp
        movl    -20(%ebp), %eax
        testl   %eax, %eax
        je      .L6
        movl    -20(%ebp), %eax
        subl    $8, %esp
        pushl   %eax
        pushl   $_ZSt4cout
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $16, %esp
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        pushl   %eax
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
.L6:
        nop
        movl    -12(%ebp), %eax
        xorl    %gs:20, %eax
        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   %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    $4, %eax  //eax=eax+4,即foo的首地址
        subl    $12, %esp
        pushl   %eax  //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   %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=[ebp+8], eax=this
        subl    $12, %esp
        pushl   %eax //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   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        subl    $24, %esp
        movl    %gs:20, %eax
        movl    %eax, -12(%ebp)
        xorl    %eax, %eax
        subl    $12, %esp
        leal    -20(%ebp), %eax  //eax=bar的首地址
        pushl   %eax  //bar.this压栈
        call    _ZN3BarC1Ev //call Bar::Bar()
        addl    $16, %esp
        movl    -16(%ebp), %eax
        testl   %eax, %eax
        je      .L6
        movl    -16(%ebp), %eax
        subl    $8, %esp
        pushl   %eax
        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   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        movl    $_ZTV6Widget+8, %edx  //edx = vptr
        movl    8(%ebp), %eax  //eax=this
        movl    %edx, (%eax)  //[this]=vptr
        nop
        popl    %ebp
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
_ZN4BellC2Ev:  //Bell:Bell()
.LFB1027:
        .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
        subl    $12, %esp //esp=esp-12
        pushl   %eax //this压栈
        call    _ZN6WidgetC2Ev  //调用base class的默认构造函数, Widget:Widget()
        addl    $16, %esp
        movl    $_ZTV4Bell+8, %edx //edx=vptr4Bell
        movl    8(%ebp), %eax //eax=this
        movl    %edx, (%eax) //[this]=vptr4Bell
        nop
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
_Z4testv:
.LFB1022:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        subl    $24, %esp
        movl    %gs:20, %eax
        movl    %eax, -12(%ebp)
        xorl    %eax, %eax
        subl    $12, %esp
        leal    -20(%ebp), %eax  //eax=ebp-20,即eax=b.this
        pushl   %eax  //b首地址压栈
        call    _ZN4BellC1Ev //Bell::Bell()
        addl    $16, %esp
        leal    -20(%ebp), %eax //eax=b.this
        movl    %eax, -24(%ebp)  //Widget &w=b
        movl    -24(%ebp), %eax  //eax=&w
        movl    (%eax), %eax  //eax=vptr4Bell
        movl    (%eax), %eax //eax = vptr4Bell[0]
        subl    $12, %esp
        pushl   -24(%ebp)  //w.this压栈
        call    *%eax   //virtual机制,w.show()
        addl    $16, %esp
        nop
        movl    -12(%ebp), %eax
        xorl    %gs:20, %eax
        je      .L5
        call    __stack_chk_fail
.L5:
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0.测试环境
  • 1, 默认构造函数在编译器需要的时产生出来
  • 2. nontrivial Default Constructor
    • 2.1 “带有Default Constructor”的Member Class Object
    • 3.”带有Default Construtor“的Base Class
    • 4. “带有virtual function”的class
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    http://www.vxiaotou.com