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

【C++】虚函数是什么?为什么需要它?

虚函数运行原理、“多态”如何实现(vtable 虚表机制),是 C++ 面向对象底层非常重要的知识点。


1. 虚函数是什么?为什么需要它?

  • 虚函数允许用父类指针/引用调用“子类自己的实现”。
  • 这就是多态(Polymorphism):同一个接口,不同的实现,运行时动态决定实际调用哪个函数。

2. 多态代码举例

class Base {
public:virtual void Print() { std::cout << "Base" << std::endl; }
};
class Derived : public Base {
public:void Print() override { std::cout << "Derived" << std::endl; }
};Base* p = new Derived();
p->Print(); // 输出 "Derived",而不是 "Base"

如果没有virtual,输出就是"Base"。有了virtual,输出"Derived"。


3. vtable(虚表)实现机制

什么是 vtable?

  • vtable(虚函数表)是编译器为每个有虚函数的类生成的一个“函数指针数组”
  • 每个对象实例里会有一个指向vtable的隐藏指针(通常叫vptr)。
  • vtable表里存放着该类所有虚函数的“实际实现函数地址”。

具体原理:

  • 当你通过父类指针/引用调用虚函数时,
  • 程序会通过对象里的vptr找到正确的vtable,然后调用vtable里实际存的子类实现地址
  • 所以最终无论用哪个指针,都会自动调到“真正的函数实现”。

4. 运行流程图解

  1. 声明类(有virtual函数)

  2. 编译时

    • 编译器为每个类生成vtable(如果有虚函数)。
    • 对象里自动加vptr,指向对应类的vtable。
  3. 运行时

    • 用基类指针/引用操作时,走vptr->vtable->实际函数地址,自动完成多态分发。

5. vtable/vptr的“模拟”代码

假设有:

class Base { virtual void f(); };
class Derived : public Base { void f() override; };

编译器背后大致做了这样的事:

typedef void(*FunPtr)();struct vtable_Base {FunPtr f;
};struct vtable_Derived {FunPtr f;
};struct Base {vtable_Base* vptr;
};struct Derived {vtable_Derived* vptr;
};

当你调用 p->f(); 时,其实是:

p->vptr->f(p);

(p作为this指针传进去)


6. 小结和常见面试点

  • 多态的底层原理: 靠虚函数表(vtable)+ 每个对象的vptr隐藏指针来实现。

  • 作用: 让“父类指针/引用指向子类对象”时,调用的是子类自己的实现。

  • 代价:

    • 每个对象多一个指针(vptr)。
    • 虚函数调用需要查表,比普通函数指针多一点点性能损耗,但几乎可以忽略。
  • 其它说明:

    • 不用virtual时,没有vtable/vptr,不支持多态。

7. 简单动图理解

Base* p = new Derived();
p->Print();-----   内存结构大致如下 -----p
│
└───> [vptr] ───> vtable (Derived类)│└──> Derived::Print
  • 编译时写成p->Print(),实际运行时会查vptr和vtable,执行Derived::Print()。

8. 调试技巧

  • sizeof 看类大小,含虚函数的类多一个指针(32位下4字节,64位下8字节)。
  • 可以用 GDB 查看对象的vptr和vtable。

9. 一张表对比

普通函数虚函数
编译时绑定运行时绑定
没有vtable/vptr有vtable/vptr
没有多态支持多态

结论

  • “虚函数 + vtable + vptr”= C++多态本质!
  • 让 父类指针/引用调用到正确的子类方法,灵活解耦、高级抽象
http://www.xdnf.cn/news/730873.html

相关文章:

  • superior哥深度学习系列(大纲)
  • NodeMediaEdge通道管理
  • 动态规划-300.最长递增子序列-力扣(LeetCode)
  • 酒店管理系统设计与实现
  • 《TCP/IP 详解 卷1:协议》第3章:链路层
  • 第十二篇:MySQL 分布式架构演进与云原生数据库探索
  • 光电学、计算机科学及算法国际会议(OCSA 2025)征稿启事​
  • 深度学习复习笔记
  • leetcode:7. 整数反转(python3解法,数学相关算法题)
  • 【笔记】Suna 部署之 Supabase 数据库 schema 暴露操作
  • day14 leetcode-hot100-26(链表5)
  • QT- QML Layout+anchors 布局+锚点实现窗口部件自适应比例
  • 阻塞队列的学习以及模拟实现一个阻塞队列
  • C++题解(34) 2025年顺德区中小学生程序设计展示活动(初中组C++)U560289 字符串排序(一)和 U560136 字符串排(二)题解
  • JVM——回顾:JVM的起源、特性与系统构成
  • 交错推理强化学习方法提升医疗大语言模型推理能力的深度分析
  • Redis-6.2.9 主从复制配置和详解
  • 65.AI流式回答后再次修改同一界面的消息不在同一对话中bug
  • 深入剖析HBase架构
  • DeviceNET转EtherCAT网关:医院药房自动化的智能升级神经中枢
  • 将材质球中的纹理属性对应的贴图保存至本地
  • 【iptables防火墙】-- URL过滤 (Hexstring、IP、DoT和DoH)
  • 用wireshark抓了个TCP通讯的包
  • 讲述我的plc自学之路 第十一章
  • 一套qt c++的串口通信
  • 新版Chrome浏览器WEB端加载DWG TrueView查看DWG、dxf工程图纸
  • CentOS 7 环境下部署 LAMP
  • RabbitMQ和MQTT区别与应用
  • MSVC支持但是Clang会报错的C++行为
  • 在 ODROID-H3+ 上安装 Win11 系统