当前位置: 首页 > article >正文

C++ 中的虚函数表(vtable)与继承:单继承与多继承的分析

在 C++ 中,虚函数表(vtable)是虚函数实现背后的关键机制。虚函数表允许在运行时实现多态,使得基类指针可以调用派生类的函数。在继承体系中,虚函数表的构建方式会随着单继承和多继承的不同情况而有所变化。

本文将详细分析在单继承与多继承下虚表的行为,并通过示例代码帮助大家更好地理解这一概念。

单继承下的虚函数表

在单继承的场景下,虚函数表的结构相对简单。派生类继承基类的虚表,如果派生类覆盖了基类的某个虚函数,派生类的虚函数地址会替换掉虚表中的对应位置。如果没有覆盖,虚表继续保留基类中的虚函数地址。

示例代码:
#include <iostream>

class Base {
public:
    virtual void func1() { std::cout << "Base::func1" << std::endl; }
    virtual void func2() { std::cout << "Base::func2" << std::endl; }
};

class Derived : public Base {
public:
    void func1() override { std::cout << "Derived::func1" << std::endl; }
};

int main() {
    Base* obj = new Derived();
    obj->func1();  // 输出:Derived::func1
    obj->func2();  // 输出:Base::func2
    return 0;
}
分析:

在这个例子中,Derived 类覆盖了 Base 类的 func1() 函数,因此 Derived::func1 的地址替换了虚表中 Base::func1 的地址。然而,func2() 没有被覆盖,因此它的虚表条目仍然指向 Base::func2

多继承下的虚函数表

在多继承的情况下,每个基类都有自己的虚函数表。派生类的虚函数表由多个基类的虚表组成,派生类覆盖的虚函数会替换对应基类的虚表条目。

示例代码:
#include <iostream>

class Base1 {
public:
    virtual void func1() { std::cout << "Base1::func1" << std::endl; }
};

class Base2 {
public:
    virtual void func2() { std::cout << "Base2::func2" << std::endl; }
};

class Derived : public Base1, public Base2 {
public:
    void func1() override { std::cout << "Derived::func1" << std::endl; }
    void func2() override { std::cout << "Derived::func2" << std::endl; }
};

int main() {
    Derived* obj = new Derived();
    Base1* b1 = obj;
    Base2* b2 = obj;

    b1->func1();  // 输出:Derived::func1
    b2->func2();  // 输出:Derived::func2

    return 0;
}
分析:

在多继承的场景下,Derived 类同时继承了 Base1Base2,并且覆盖了两个基类的虚函数。因此,Base1Base2 各自的虚表中的相应条目被 Derived 中的虚函数地址替换。

每个基类维护自己的虚表,并且 Derived 类会根据继承顺序将虚函数存储在相应基类的虚表中。

虚函数表的布局

单继承时虚函数表的布局:
Base:
+------------+
| Base::func1|
| Base::func2|
+------------+

Derived (继承自 Base):
+------------+
| Derived::func1 |  // 覆盖了 Base::func1
| Base::func2    |  // 没有覆盖,继续使用 Base::func2
+------------+
多继承时虚函数表的布局:
Base1:
+------------+
| Base1::func1 |
+------------+

Base2:
+------------+
| Base2::func2 |
+------------+

Derived (继承自 Base1 和 Base2):
Base1's vtable:
+------------+
| Derived::func1 |  // 覆盖了 Base1::func1
+------------+

Base2's vtable:
+------------+
| Derived::func2 |  // 覆盖了 Base2::func2
+------------+

总结

通过本文的分析,我们可以总结出以下几点:

  1. 在单继承中,派生类的虚表继承自基类,并且派生类覆盖的虚函数地址会替换基类虚表中的对应条目。
  2. 在多继承中,每个基类有自己独立的虚表。派生类覆盖的虚函数会替换对应基类虚表中的函数地址。
  3. 虚表中的函数按照声明顺序存放,基类的虚函数优先存放在表中,派生类的虚函数则替换相应位置或附加到表中。

理解虚函数表的工作原理有助于我们优化和设计 C++ 多态结构,避免潜在的二义性和继承关系中的复杂性。


http://www.kler.cn/news/358271.html

相关文章:

  • 智简魔方业务管理系统v10 好用的IDC业务管理软件
  • ZigZagOnParabolic,MetaTrader 免费公式!(指标教程)
  • 【力扣打卡系列】滑动窗口与双指针(乘积小于K的子数组)
  • Spring boot 控制台输出大佛
  • QT 对话框
  • [k8s理论知识]3.docker基础(二)隔离技术
  • 百度SEO前10关键词排名波动跟用户行为反馈有很大关系
  • UG NX12.0建模入门笔记:1.2 鼠标的基本操作
  • 【VUE小型网站开发】初始环境搭建
  • RNN,LSTM,GRU的区别和联系? RNN的梯度消失问题?如何解决?
  • Widget结构(一)
  • 基于SpringBoot+Vue的厨艺交流系统的设计与实现(源码+定制开发)厨艺知识与美食交流系统开发、在线厨艺分享与交流平台开发、智能厨艺交流与分享系统开发
  • 【文心智能体 | AI大师工坊】如何使用智能体插件,完成一款购物类智能体的开发,来体验一下我的智能体『科技君Tom』
  • 爬虫实现验证码登录古诗文网【爬虫学习day.02】
  • 【高等数学】无穷级数
  • Win11 安装 PostgreSQL 报错解决方案
  • 【小洛的VLOG】Web 服务器高并发压力测试(Reactor模型测试)
  • 【环境搭建】Windows系统中使用VScode在虚拟机ubuntu系统中进行开发的方法
  • C++ 算法学习——1.9 Kruskal算法
  • 平安养老险深圳分公司:创新养老服务,深入践行金融为民