对象内存结构及虚函数表分析

2023/04/21 c++ 共 3469 字,约 10 分钟

C++ 对象结构模型与虚函数表的实例解析

无继承类对象的内存结构

先来看看有与没有虚函数的类的对象的内存结构的不同之处:

无虚函数的对象

内存结构:

Object with no virtual function

验证如下:

#include <stdio.h>

class CObj
{
public:
    CObj(int nData1, int nData2) : m_nData1(nData1), m_nData2(nData2) {}
    ~CObj() {}

    void Func() { printf("CObj::Func()\n"); }

private:
    int m_nData1;
    int m_nData2;
};

int main()
{
    CObj obj(11 ,22);

    return 0;
}

来看看 obj 的实际内存分布:

Object with no virtual function

小结:

  • 数据成员按声明顺序排列

有虚函数的对象

内存结构:

Object with virtual functions

验证如下:

#include <stdio.h>

class CObj
{
public:
    CObj(int nData1, int nData2) : m_nData1(nData1), m_nData2(nData2) {}
    ~CObj() {}

    virtual void FuncA() { printf("CObj::FuncA()\n"); }
    virtual void FuncB() { printf("CObj::FuncB()\n"); }

private:
    int m_nData1;
    int m_nData2;
};

int main()
{
    CObj obj1(10,20);
    CObj obj2(11,22);

    return 0;
}

来看看 obj1 和 obj2 的实际内存结构:

Object with virtual functions

小结:

  • 虚函数指针在虚表内按声明顺序排列

单继承的类对象的内存结构

子类覆盖父类虚函数之后虚函数表的变化可以通过对比明显的得出,这即是多态的实现机制。

子类无覆盖父类的虚函数

内存结构:

derive no override

验证如下:

#include <stdio.h>

class CBase
{
public:
    CBase() { m_nData1 = 10; }
    virtual void FuncA() { printf("CBase::FuncA\n"); }

private:
    int m_nData1;
};

class CDerive : public CBase
{
public:
    CDerive() { m_nData2 = 20; }
    virtual void FuncB() { printf("CDerive::FuncB\n"); }

private:
    int m_nData2;
};

int main()
{
    CDerive *pDerive = new CDerive;

    return 0;
}

来看看 pDerive 的实际内存结构:

derive no override

小结:

  • 父类成员在子类成员之前
  • 父类虚函数在子类虚函数之前

子类有覆盖父类的虚函数

内存结构:

derive override

验证如下:

#include <stdio.h>

class CBase
{
public:
    CBase() { m_nData1 = 10; }
    virtual void FuncA() { printf("CBase::FuncA\n"); }

private:
    int m_nData1;
};

class CDerive : public CBase
{
public:
    CDerive() { m_nData2 = 20; }
    virtual void FuncA() { printf("CDerive::FuncA\n"); }    // add this line
    virtual void FuncB() { printf("CDerive::FuncB\n"); }

private:
    int m_nData2;
};

int main()
{
    CDerive *pDerive = new CDerive;

    return 0;
}

来看看 pDerive 的实际内存结构:

derive override

普通多继承的类对象的内存结构

内存结构:

multi derive

验证如下:

#include <stdio.h>

class CBase1
{
public:
    CBase1() { m_nData1 = 10; }
    virtual void FuncA() { printf("CBase1::FuncA\n"); }

private:
    int m_nData1;
};

class CBase2
{
public:
    CBase2() { m_nData2 = 20; }
    virtual void FuncB() { printf("CBase2::FuncB\n"); }

private:
    int m_nData2;
};

class CDerive : public CBase1, public CBase2
{
public:
    CDerive() { m_nData3 = 30; }
    virtual void FuncA() { printf("CDerive::FuncA\n"); }
    virtual void FuncB() { printf("CDerive::FuncB\n"); }
    virtual void FuncC() { printf("CDerive::FuncC\n"); }

private:
    int m_nData3;
};

int main()
{
    CDerive *pDerive = new CDerive;
    CBase1 *pBase1 = pDerive;
    CBase2 *pBase2 = pDerive;

    return 0;
}

来看看 pDerive、pBase1 和 pBase2 在实际内存中的情况:

multi derive

小结:

  • 多个父类的成员在内存中按继承时声明的顺序排列
  • 子类数据成员放在最后一个父类的数据成员之后
  • 子类虚函数列表在第一个虚表中
  • 第一张虚表中存放了所有的虚函数指针,其它虚表存放了某个父类的(可能是被覆盖后的)虚函数指针

单虚继承的类对象的内存结构

内存结构:

virtual derive

验证如下:

#include <stdio.h>

class CBase
{
public:
    CBase() { m_nData1 = 10; }
    virtual void FuncA() { printf("CBase::FuncA\n"); }

private:
    int m_nData1;
};

class CDerive : public virtual CBase
{
public:
    CDerive() { m_nData2 = 20; }
    virtual void FuncA() { printf("CDerive::FuncA\n"); }
    virtual void FuncB() { printf("CDerive::FuncB\n"); }

private:
    int m_nData2;
};

int main()
{
    CDerive *pDerive = new CDerive;
    CBase *pBase = pDerive;

    return 0;
}

来看看 pDerive、pBase 在实际内存中的情况:

virtual derive

小结:

  • 父类数据成员会放在第二张虚表指针之后
  • 第一张虚表里存放了所有的虚函数指针

钻石结构的类对象的内存结构

内存结构:

diamond derive

验证如下:

#include <stdio.h>

class CBase
{
public:
    CBase() { m_nData = 1; }
    virtual void Func() { printf("CBase::Func\n"); }

private:
    int m_nData;
};

class CBase1 : virtual public CBase
{
public:
    CBase1() { m_nData1 = 10; }
    virtual void FuncA() { printf("CBase1::FuncA\n"); }

private:
    int m_nData1;
};

class CBase2 : virtual public CBase
{
public:
    CBase2() { m_nData2 = 20; }
    virtual void FuncB() { printf("CBase2::FuncB\n"); }

private:
    int m_nData2;
};

class CDerive : public CBase1, public CBase2
{
public:
    CDerive() { m_nData3 = 30; }
    virtual void FuncA() { printf("CDerive::FuncA\n"); }
    virtual void FuncB() { printf("CDerive::FuncB\n"); }
    virtual void FuncC() { printf("CDerive::FuncC\n"); }

private:
    int m_nData3;
};

int main()
{
    CDerive *pDerive = new CDerive;
    CBase *pBase = pDerive;
    CBase1 *pBase1 = pDerive;
    CBase2 *pBase2 = pDerive;

    return 0;
}

来看看 pDerive、pBase、pBase1 和 pBase2 在实际内存中的情况:

diamond derive

小结:

  • 第一张虚表里没有存放根父类的虚函数指针

文档信息

Search

    Table of Contents