前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对象内存结构及虚函数表分析

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

作者头像
零式的天空
发布2022-03-28 20:23:21
2320
发布2022-03-28 20:23:21
举报
文章被收录于专栏:零域Blog

最近抽空浏览了一遍《COM 原理与应用》,一本老书了,COM 技术在我工作中运用得不多,但是接口设计规范和标准这一套东西还是能带给我一些有用的实践经验和启发的。在读到第二章《COM 对象和接口》的时候,看到虚函数表的一些相关知识,这些之前倒是也都知道,但是从来没有试着自己描述过,所以老觉得理解得不够彻底,那么……就试着结合一些小的代码段描述一下看,权当笔记加深记忆。

以下代码及运行结果基于 Win7_64 + GCC 4.7.2 环境,其它环境下可能程序运行结果等有出入,但是原理相通。

目录

  • TOC {:toc}

无继承类对象的内存结构

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

无虚函数的对象

验证如下:

代码语言:javascript
复制
#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;
}

小结:

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

有虚函数的对象

验证如下:

代码语言:javascript
复制
#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;
}

小结:

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

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

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

子类无覆盖父类的虚函数

验证如下:

代码语言:javascript
复制
#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;
}

小结:

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

子类有覆盖父类的虚函数

验证如下:

代码语言:javascript
复制
#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;
}

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

验证如下:

代码语言:javascript
复制
#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;
}

小结:

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

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

验证如下:

代码语言:javascript
复制
#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;
}

小结:

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

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

验证如下:

代码语言:javascript
复制
#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;
}

小结:

  • 第一张虚表里没有存放根父类的虚函数指针
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 无继承类对象的内存结构
  • 单继承的类对象的内存结构
  • 普通多继承的类对象的内存结构
  • 单虚继承的类对象的内存结构
  • 钻石结构的类对象的内存结构
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档