C++对象模型-Non-Staitc成员函数

2022-04-25 15:16:37 浏览数 (1)

开发环境

gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)

1, non-static成员函数调用过程

class X定义了一个virtual function foo:函数调用过程(栈帧)class X定义了一个virtual function foo:

代码语言:javascript复制
#include<iostream>
using namespace std;
//不分析virtual dctor
class X{
        public:
                virtual void foo();
        private:
                int _x;
};
void X::foo()
{
        cout<<_x<<endl;
}
X foobar()
{
        X xx;
        X *px = new X;
        xx.foo();
        px->foo();
        delete px;
        return xx;
}
int main()
{
        foobar();
        return 0;
}

该程序对应的汇编代码如下(g -S -m32 p13.cc):

代码语言:javascript复制
_ZN1X3fooEv:  //X::foo()
.LFB1021:
        .cfi_startproc
        pushl   �p  //ebp压栈([esp]=ebp, esp=esp-4)
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, �p  //ebp=esp
        .cfi_def_cfa_register 5
        subl    $8, %esp  //esp=esp-8
        movl    8(�p), �x  //eax=[ebp 8],即eax=this
        movl    4(�x), �x  //eax=[eax 4], 即eax=this->_x
        subl    $8, %esp  //esp=esp-8
        pushl   �x  //[esp]=eax, esp=esp-4, 即this->_x压栈
        pushl   $_ZSt4cout //std::cout压栈
        call    _ZNSolsEi  //operator<< 对应两步操作:1,retAddr压栈 2,IP=operator<< func
        addl    $16, %esp
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        pushl   �x
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        nop
        leave  //ebp=esp, pop ebp
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret //pop IP
        .cfi_endproc
.LFE1021:
        .size   _ZN1X3fooEv, .-_ZN1X3fooEv
        .section        .text._ZN1XC2Ev,"axG",@progbits,_ZN1XC5Ev,comdat
        .align 2
        .weak   _ZN1XC2Ev
        .type   _ZN1XC2Ev, @function
代码语言:javascript复制
_ZN1XC2Ev: //X::X() 默认构造函数
.LFB1024:
        .cfi_startproc
        pushl   �p //ebp压栈
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, �p //ebp = esp
        .cfi_def_cfa_register 5
        movl    $_ZTV1X 8, �x  //edx=vptr
        movl    8(�p), �x  //eax=[ebp 8],即eax=this
        movl    �x, (�x) //[eax]=edx, 即[this]=vptr
        nop
        popl    �p  //ebp=old ebp
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
.LFE1024:
        .size   _ZN1XC2Ev, .-_ZN1XC2Ev
        .weak   _ZN1XC1Ev
        .set    _ZN1XC1Ev,_ZN1XC2Ev
        .text
        .globl  _Z6foobarv
        .type   _Z6foobarv, @function
代码语言:javascript复制
_Z6foobarv: //foobar
.LFB1022:
        .cfi_startproc
        pushl   �p  //ebp压栈
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, �p //ebp=esp
        .cfi_def_cfa_register 5
        pushl   �x  //ebx压栈
        subl    $36, %esp //esp=esp-36
        .cfi_offset 3, -12
        movl    8(�p), �x //eax=tmp.this (返回值的首地址,即this指针)
        movl    �x, -28(�p) //[ebp-28]=tmp.this
        movl    %gs:20, �x
        movl    �x, -12(�p)
        xorl    �x, �x //eax=0
        subl    $12, %esp  //esp=esp-12
        pushl   -28(�p) //push xx.this, 即xx对象首地址压栈
        call    _ZN1XC1Ev //X::X(), 即默认构造函数
        addl    $16, %esp
        subl    $12, %esp
        pushl   $8  //分配内存operator new size=8,
        call    _Znwj //operator new
        addl    $16, %esp
        movl    �x, �x  //eax为new函数返回值(px.this指针), ebx=eax,
        subl    $12, %esp
        pushl   �x  //push px.this
        call    _ZN1XC1Ev   //X::X(),构造px指针指向的对象
        addl    $16, %esp
        movl    �x, -16(�p)  //[ebp-16]=px.this
        subl    $12, %esp
        pushl   -28(�p) //xx对象this指针压栈
        call    _ZN1X3fooEv  //xx.foo()
        addl    $16, %esp
        movl    -16(�p), �x //eax=px.this
        movl    (�x), �x //eax=vptr
        movl    (�x), �x  //eax=[vptr],即eax=virtual foo()
        subl    $12, %esp
        pushl   -16(�p) //px.this压栈
        call    *�x  //call virtual foo
        addl    $16, %esp
        subl    $12, %esp
        pushl   -16(�p)
        call    _ZdlPv  //delete px
        addl    $16, %esp
        nop
        movl    -28(�p), �x
        movl    -12(�p), �x
        xorl    %gs:20, �x
        je      .L5
        call    __stack_chk_fail
.L5:
        movl    -4(�p), �x
        leave
        .cfi_restore 5
        .cfi_restore 3
        .cfi_def_cfa 4, 4
        ret     $4
        .cfi_endproc
代码语言:javascript复制
main:
.LFB1029:
        .cfi_startproc
        leal    4(%esp), �x
        .cfi_def_cfa 1, 0
        andl    $-16, %esp
        pushl   -4(�x)
        pushl   �p
        .cfi_escape 0x10,0x5,0x2,0x75,0
        movl    %esp, �p
        pushl   �x
        .cfi_escape 0xf,0x3,0x75,0x7c,0x6
        subl    $36, %esp
        movl    %gs:20, �x
        movl    �x, -12(�p)
        xorl    �x, �x
        leal    -40(�p), �x
        subl    $12, %esp
        pushl   �x  //tmp.this压栈
        call    _Z6foobarv //foobar
        addl    $12, %esp
        movl    $0, �x
        movl    -12(�p), �x
        xorl    %gs:20, �x
        je      .L8
        call    __stack_chk_fail
.L8:
        movl    -4(�p), �x
        .cfi_def_cfa 1, 0
        leave
        .cfi_restore 5
        leal    -4(�x), %esp
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
2,通过分析cpp代码对应的汇编,发现foobar函数在内部被转化为:
代码语言:javascript复制
void foobar(X *result)
{
    //构造result指向的对象
    X::X(result);
    
    
    px = malloc(sizeof(X));
    X::X(px);
   
    //xx.foo()不采用virtual机制
    X::foo(result);
    
    //px->foo()采用virtual机制
    (px->vptr[0])(px);
  
    //略
    
}

0 人点赞