c++ 虚函数内存浅析(一)

本文探讨C++中虚函数的内存布局,包括构造和析构函数的顺序,以及虚函数表的创建。在简单模式下,每个类都有自己的虚函数表,子类构造时会更新表指针,且同名虚函数在不同表中的偏移相同。调用虚函数时通过虚函数表寻址。文章附带内存布局图解,展示成员变量和虚函数的排列方式。

c++ 虚函数内存浅析(一)


一:引子

   c++和其他面向对象的语言一样,都有多态,封装,继承的特性,多态主要靠重载和覆盖来实现。
   如果用我自己的理解来对多态进行定义,那么“根据不同的上下文语境,使用不同的同名函数
   需求的一种方法。”这样的说法可能更容易理解一些。虚函数是实现重载和覆盖的重要手段,
   那么对于虚函数,编译器做了哪些工作;以及代码在cpu上到底是如何实现的呢?

二:正文

简单模式下的内存情景

  • 构造,析构函数顺序
    • 测试代码
      类的继承关系如下:
class BaseTestA
{
public:

    BaseTestA()
    {
        m_countA = 0xa0;
        printf("BaseTestA in \n");
    }

    ~BaseTestA()
    {
        printf("BaseTestA out \n");
    }

    void PrintFun()
    {
        printf("BaseTestA \n");
    }

    virtual void virPrint()
    {
        printf("Base virtual print A \n");
    }
    int m_countA;
};

// 单线继承
class TestB : public BaseTestA
{
public:

    TestB()
    {
        m_countB = 0xb0;
        printf("TestB in \n");
    }

    ~TestB()
    {
        printf("TestB out \n");
    }

    virtual void PrintFun()
    {
        printf("TestB \n");
    }

    virtual void virPrint()
    {
        printf("virPrint print B \n");
    }

    virtual void virRun()
    {
        printf("virRun print B \n");
    }

    int m_countB;
};

执行测试代码:

void test()
{
    BaseTestA *testA = new TestB;
    testA->PrintFun();
    testA->virPrint();
    TestB testB;
    testB.PrintFun();
}

int _tmain(int argc, _TCHAR* argv[])
{
    test();
    // 等待结果查看
    char c = getchar();
    return 0;
}

这里写图片描述

执行顺序: 父类的构造函数->子类的构造函数; 子类的析构函数->父类的析构函数。
简单模式下的顺序比较简单,有兴趣的可以用od或者windbg详细跟踪下一个子类的建立与消亡的过程。
在构造函数的跟踪过程中会发现:
1. 每一个类都会有一个虚函数表,对每一个类的构造时,都会将+0偏移的位置用虚函数表指针写入。
2. 子类的虚函数表和父类的虚函数表中,同名的函数在各自的虚函数表中的偏移是相同的。

虚函数在调用时,是this调用方式,一般将ecx作为this指针进行寄存器传参,而且虚函数在调用寻址时都是从虚函数表中的偏移获取所得如:
mov ecx,eax
call [eax+0x68]

  • 内存布局
    简单情形下的内存布局也比较简单,在这里发图说明:
    这里写图片描述

    成员变量和虚函数的顺序都是 父类在上,依次往下子类的。
    如果父类和子类有虚函数是覆盖或者重载实现的,那么子类的虚函数会覆盖掉父类的同名虚函数。
    这个是在编译器自动完成的,也就是说,如果一个exe生成了,那么每一个类的虚函数表都是固定值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值