C++中虚成员函数调用方式

C++中虚成员函数调用方式

只有一个虚函数

/* ************************************************************************
> File Name:     test.cpp
> Author:        niu0217
> Created Time:  Tue 06 Feb 2024 08:54:26 PM CST
> Description:
 ************************************************************************/

#include<iostream>
using namespace std;

class MYACLS {
public:
    int m_i;
    void myfunc(int value) {
        this->m_i += value;
    }
    virtual void vmyfunc() {
        printf("vmyfunc()被调用,this = %p\n", this);
    }
};

int main()
{
    //用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表
    MYACLS myacls;
    myacls.vmyfunc();

    MYACLS* pmyacls = new MYACLS();
    pmyacls->vmyfunc();
    delete pmyacls;
    pmyacls = nullptr;

    return 0;
}

调试:

In file: /home/ubuntu/Dev/C++ObjectModern/function_semantics/test.cpp
   21
   22 int main()
   23 {
   24     //用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表
   25     MYACLS myacls;
 ► 26     myacls.vmyfunc();
   27
   28     MYACLS* pmyacls = new MYACLS();
   29     pmyacls->vmyfunc();
   30     delete pmyacls;
   31     pmyacls = nullptr;
   
=> 0x0000555555555211 <+40>:	lea    rax,[rbp-0x30]
   0x0000555555555215 <+44>:	mov    rdi,rax
   0x0000555555555218 <+47>:	call   0x5555555552fc <MYACLS::vmyfunc()>

继续:

In file: /home/ubuntu/Dev/C++ObjectModern/function_semantics/test.cpp
   24     //用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表
   25     MYACLS myacls;
   26     myacls.vmyfunc();
   27
   28     MYACLS* pmyacls = new MYACLS();
 ► 29     pmyacls->vmyfunc();
   30     delete pmyacls;
   31     pmyacls = nullptr;
   32
   33     return 0;
   34 }
   
   # rax = pmyacls = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   # rax中的值0x55555556b2c0又被叫做this指针
=> 0x0000555555555244 <+91>:	mov    rax,QWORD PTR [rbp-0x38]
   # rax = [rax] = [this指针] = [0x55555556b2c0] = 0x0000555555557d70 = 虚表指针 = vptr
   0x0000555555555248 <+95>:	mov    rax,QWORD PTR [rax]
   # rdx = [rax] = [0x0000555555557d70] = 0x00005555555552fc = vptr[0] = MYACLS::vmyfunc()的函数指针
   0x000055555555524b <+98>:	mov    rdx,QWORD PTR [rax]
   # rax = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x000055555555524e <+101>:	mov    rax,QWORD PTR [rbp-0x38]
   # rdi = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x0000555555555252 <+105>:	mov    rdi,rax
   # call MYACLS::vmyfunc()的函数指针
   0x0000555555555255 <+108>:	call   rdx
   
# 虚函数表的样子
# pmyacls = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
# vptr = 0x555555557d70 = 虚表指针 = vptr
# vptr[0] = 0x5555555552fc = [0x555555557d70 + 0x0] = MYACLS::vmyfunc()的函数指针
pwndbg> info vtbl pmyacls
vtable for 'MYACLS' @ 0x555555557d70 (subobject @ 0x55555556b2c0):
[0]: 0x5555555552fc <MYACLS::vmyfunc()>

# pmyacls = 0x55555556b2c0
pwndbg> p pmyacls
$1 = (MYACLS *) 0x55555556b2c0

# vptr = 0x555555557d70
pwndbg> x/gx 0x55555556b2c0
0x55555556b2c0:	0x0000555555557d70

# vptr[0] = 0x5555555552fc = [0x555555557d70 + 0x0] = MYACLS::vmyfunc()的函数指针
pwndbg> x/gx 0x0000555555557d70
0x555555557d70 <vtable for MYACLS+16>:	0x00005555555552fc
pwndbg> info line *0x00005555555552fc
Line 17 of "test.cpp" starts at address 0x5555555552fc <MYACLS::vmyfunc()> and ends at 0x55555555530c <MYACLS::vmyfunc()+16>.

有4个虚函数

/* ************************************************************************
> File Name:     test.cpp
> Author:        niu0217
> Created Time:  Tue 06 Feb 2024 08:54:26 PM CST
> Description:
 ************************************************************************/

#include<iostream>
using namespace std;

class MYACLS {
public:
    int m_i;
    void myfunc() {
        printf("myfunc()被调用,this = %p\n", this);
    }
    virtual void vmyfunc1() {
        printf("vmyfunc1()被调用,this = %p\n", this);
    }
    virtual void vmyfunc2() {
        printf("vmyfunc2()被调用,this = %p\n", this);
    }
    virtual void vmyfunc3() {
        printf("vmyfunc3()被调用,this = %p\n", this);
    }
    virtual void vmyfunc4() {
        printf("vmyfunc4()被调用,this = %p\n", this);
    }
};

int main()
{
    //用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表
    MYACLS myacls;
    myacls.myfunc();
    myacls.vmyfunc1();
    myacls.vmyfunc2();
    cout<<endl;

    MYACLS* pmyacls = new MYACLS();
    pmyacls->myfunc();
    pmyacls->vmyfunc1();
    pmyacls->vmyfunc2();
    pmyacls->vmyfunc3();
    pmyacls->vmyfunc4();

    delete pmyacls;
    pmyacls = nullptr;

    return 0;
}

调试:

In file: /home/ubuntu/Dev/C++ObjectModern/function_semantics/test.cpp
   30
   31 int main()
   32 {
   33     //用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表
   34     MYACLS myacls;
 ► 35     myacls.myfunc();
   36     myacls.vmyfunc1();
   37     myacls.vmyfunc2();
   38     cout<<endl;
   39
   40     MYACLS* pmyacls = new MYACLS();
   
   # myacls.myfunc();
=> 0x0000555555555231 <+40>:	lea    rax,[rbp-0x30]
   0x0000555555555235 <+44>:	mov    rdi,rax
   0x0000555555555238 <+47>:	call   0x55555555539a <MYACLS::myfunc()>
   
   # myacls.vmyfunc1();
   0x000055555555523d <+52>:	lea    rax,[rbp-0x30]
   0x0000555555555241 <+56>:	mov    rdi,rax
   0x0000555555555244 <+59>:	call   0x5555555553c6 <MYACLS::vmyfunc1()>
   
   # myacls.vmyfunc2();
   0x0000555555555249 <+64>:	lea    rax,[rbp-0x30]
   0x000055555555524d <+68>:	mov    rdi,rax
   0x0000555555555250 <+71>:	call   0x5555555553f2 <MYACLS::vmyfunc2()>

从这段汇编代码我们看到,用对象调用虚函数,和调用普通函数没有区别,不会使用虚函数表。

继续:

In file: /home/ubuntu/Dev/C++ObjectModern/function_semantics/test.cpp
   36     myacls.vmyfunc1();
   37     myacls.vmyfunc2();
   38     cout<<endl;
   39
   40     MYACLS* pmyacls = new MYACLS();
 ► 41     pmyacls->myfunc();
   42     pmyacls->vmyfunc1();
   43     pmyacls->vmyfunc2();
   44     pmyacls->vmyfunc3();
   45     pmyacls->vmyfunc4();
   46
   
   # pmyacls->myfunc();
=> 0x0000555555555292 <+137>:	mov    rax,QWORD PTR [rbp-0x38]
   0x0000555555555296 <+141>:	mov    rdi,rax
   0x0000555555555299 <+144>:	call   0x55555555539a <MYACLS::myfunc()>
   
   
   # pmyacls->vmyfunc1();
   # rax = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x000055555555529e <+149>:	mov    rax,QWORD PTR [rbp-0x38]
   # rax = [this指针] = [0x55555556b2c0 + 0x0] = vptr = 0x555555557d48 = 虚表指针
   0x00005555555552a2 <+153>:	mov    rax,QWORD PTR [rax]
   # rdx = vptr[0] = 0x5555555553c6 = [0x555555557d48 + 0x0] = MYACLS::vmyfunc1()
   0x00005555555552a5 <+156>:	mov    rdx,QWORD PTR [rax]
   # rax = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552a8 <+159>:	mov    rax,QWORD PTR [rbp-0x38]
   # rdi = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552ac <+163>:	mov    rdi,rax
   # call MYACLS::vmyfunc1()
   0x00005555555552af <+166>:	call   rdx
   
   
   # pmyacls->vmyfunc2();
   # rax = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552b1 <+168>:	mov    rax,QWORD PTR [rbp-0x38]
   # rax = [this指针] = [0x55555556b2c0 + 0x0] = vptr = 0x555555557d48 = 虚表指针
   0x00005555555552b5 <+172>:	mov    rax,QWORD PTR [rax]
   # rax = &vptr[1] = 0x555555557d48 + 0x8
   0x00005555555552b8 <+175>:	add    rax,0x8
   # rdx = vptr[1] = 0x5555555553f2 = [0x555555557d48 + 0x8] = MYACLS::vmyfunc2()
   0x00005555555552bc <+179>:	mov    rdx,QWORD PTR [rax]
   # rax = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552bf <+182>:	mov    rax,QWORD PTR [rbp-0x38]
   # rdi = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552c3 <+186>:	mov    rdi,rax
   # call MYACLS::vmyfunc2()
   0x00005555555552c6 <+189>:	call   rdx
   
   
   # pmyacls->vmyfunc3();
   # rax = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552c8 <+191>:	mov    rax,QWORD PTR [rbp-0x38]
   # rax = [this指针] = [0x55555556b2c0 + 0x0] = vptr = 0x555555557d48 = 虚表指针
   0x00005555555552cc <+195>:	mov    rax,QWORD PTR [rax]
   # rax = &vptr[2] = 0x555555557d48 + 0x10
   0x00005555555552cf <+198>:	add    rax,0x10
   # rdx = vptr[2] = 0x55555555541e = [0x555555557d48 + 0x10] = MYACLS::vmyfunc3()
   0x00005555555552d3 <+202>:	mov    rdx,QWORD PTR [rax]
   # rax = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552d6 <+205>:	mov    rax,QWORD PTR [rbp-0x38]
   # rdi = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552da <+209>:	mov    rdi,rax
   # call MYACLS::vmyfunc3()
   0x00005555555552dd <+212>:	call   rdx
   
   
   # pmyacls->vmyfunc4();
   # rax = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552df <+214>:	mov    rax,QWORD PTR [rbp-0x38]
   # rax = [this指针] = [0x55555556b2c0 + 0x0] = vptr = 0x555555557d48 = 虚表指针
   0x00005555555552e3 <+218>:	mov    rax,QWORD PTR [rax]
   # rax = &vptr[3] = 0x555555557d48 + 0x18
   0x00005555555552e6 <+221>:	add    rax,0x18
   # rdx = vptr[3] = 0x55555555544a = [0x555555557d48 + 0x18] = MYACLS::vmyfunc4()
   0x00005555555552ea <+225>:	mov    rdx,QWORD PTR [rax]
   # rax = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552ed <+228>:	mov    rax,QWORD PTR [rbp-0x38]
   # rdi = this指针 = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
   0x00005555555552f1 <+232>:	mov    rdi,rax
   # call MYACLS::vmyfunc4()
   0x00005555555552f4 <+235>:	call   rdx
# 虚函数表的样子
# pmyacls = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
# vptr = [0x55555556b2c0 + 0x0] = 0x555555557d48 = 虚表指针
# vptr[0] = 0x5555555553c6 = [0x555555557d48 + 0x0] = MYACLS::vmyfunc1()
# vptr[1] = 0x5555555553f2 = [0x555555557d48 + 0x8] = MYACLS::vmyfunc2()
# vptr[2] = 0x55555555541e = [0x555555557d48 + 0x10] = MYACLS::vmyfunc3()
# vptr[3] = 0x55555555544a = [0x555555557d48 + 0x18] = MYACLS::vmyfunc4()
pwndbg> info vtbl pmyacls
vtable for 'MYACLS' @ 0x555555557d48 (subobject @ 0x55555556b2c0):
[0]: 0x5555555553c6 <MYACLS::vmyfunc1()>
[1]: 0x5555555553f2 <MYACLS::vmyfunc2()>
[2]: 0x55555555541e <MYACLS::vmyfunc3()>
[3]: 0x55555555544a <MYACLS::vmyfunc4()>


# pmyacls = 0x55555556b2c0 = 在堆上申请的一个MYACLS对象的地址
pwndbg> p pmyacls
$2 = (MYACLS *) 0x55555556b2c0

# vptr = [0x55555556b2c0 + 0x0] = 0x555555557d48 = 虚表指针
pwndbg> x/gx 0x55555556b2c0
0x55555556b2c0:	0x0000555555557d48

# vptr[0] = 0x5555555553c6 = [0x555555557d48 + 0x0] = MYACLS::vmyfunc1()
# vptr[1] = 0x5555555553f2 = [0x555555557d48 + 0x8] = MYACLS::vmyfunc2()
# vptr[2] = 0x55555555541e = [0x555555557d48 + 0x10] = MYACLS::vmyfunc3()
# vptr[3] = 0x55555555544a = [0x555555557d48 + 0x18] = MYACLS::vmyfunc4()
pwndbg> x/4gx 0x0000555555557d48
0x555555557d48 <vtable for MYACLS+16>:	0x00005555555553c6	0x00005555555553f2
0x555555557d58 <vtable for MYACLS+32>:	0x000055555555541e	0x000055555555544a
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落后的炫幕あ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值