跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 多态核心解析:虚函数重写与动态绑定原理

C++ 多态分为编译时与运行时两种。运行时多态依赖基类指针或引用调用虚函数,通过虚函数表(vtable)在运行时确定具体函数地址。虚函数定义、重写规则、纯虚函数与抽象类,并深入剖析虚表指针、动态绑定及内存布局原理,涵盖 override 与 final 关键字用法,帮助理解面向对象核心机制。

魔法巫师发布于 2026/3/22更新于 2026/5/64 浏览
C++ 多态核心解析:虚函数重写与动态绑定原理

C++ 多态核心解析:虚函数重写与动态绑定原理

多态分为编译时多态(静态)和运行时多态(动态)。编译时多态主要涉及函数重载和模板,而运行时多态则是本篇的重点。

运行时多态允许通过相同的接口调用不同的实现。例如,同样是'买票'行为,普通用户全价,学生优惠,军人优先。在代码层面,这意味着传入不同类型的对象,调用同一个函数名,却执行不同的逻辑。

多态的定义及实现条件

多态通常发生在继承体系下的类对象之间。要实现运行时多态,必须满足两个核心条件:

  1. 必须是基类的指针或引用调用虚函数。
  2. 被调用的函数必须是虚函数,且在派生类中完成了重写(Override)。

虚函数基础

在类成员函数前加 virtual 修饰符,该函数即成为虚函数。非成员函数不能声明为虚函数。

class Person {
public:
    virtual void BuyTicket() {
        cout << "买票 - 全价" << endl;
    }
};

虚函数的重写

当派生类中存在一个与基类虚函数签名完全相同(返回值类型、函数名、参数列表一致)的函数时,称为重写。即使派生类未显式添加 virtual 关键字,只要继承了基类的虚函数,依然构成重写,但建议显式声明以保持规范。

namespace demo {
class Person {
public:
    virtual void BuyTicket() {
        cout << "买票 - 全价" << endl;
    }
};

class Student : public Person {
public:
    virtual void BuyTicket() {
        cout << "买票 - 打折" << endl;
    }
};

class Animal {
public:
    virtual void talk() const {}
};

  :  Animal {
:
    {
        std::cout <<  << std::endl;
    }
};

  :  Animal {
:
    {
        std::cout <<  << std::endl;
    }
};

{
    animal.();
}
} 

{
    demo::Cat cat;
    demo::Dog dog;
    demo::(cat);
    demo::(dog);
     ;
}
class
Dog
public
public
virtual void talk() const
"汪汪"
class
Cat
public
public
virtual void talk() const
"(>^ω^<)喵"
void letHear(const Animal& animal)
talk
// namespace demo
int main()
letHear
letHear
return
0

其他重写问题

协变(Covariant Return Types) 派生类重写基类虚函数时,如果基类返回基类对象的指针或引用,派生类可以返回派生类对象的指针或引用。这被称为协变。虽然实际意义有限,但了解其存在有助于处理复杂继承关系。

析构函数的重写 若基类析构函数声明为虚函数,则派生类析构函数无论是否加 virtual,均构成重写。编译器会对析构函数名称做特殊处理,确保正确销毁派生类资源。

override 和 final 关键字

C++11 引入了 override 和 final 来增强安全性。

  • override:强制检查是否成功重写了基类虚函数。如果签名不匹配,编译器会报错,避免静默失败。
  • final:阻止后续派生类继续重写当前虚函数。
// error: 方法没有重写任何基类方法
class Car {
public:
    virtual void Drive() {}
};

class Benz : public Car {
public:
    virtual void Drive() override {
        cout << "Benz-舒适" << endl;
    }
};

// error: 无法被'Benz::Drive'重写
class Car2 {
public:
    virtual void Drive() final {}
};

class Benz2 : public Car2 {
public:
    virtual void Drive() {
        cout << "Benz-舒适" << endl;
    }
};

重载、重写与隐藏的区别

特性重载 (Overload)重写 (Override)隐藏 (Hide)
作用域同一类内基类与派生类基类与派生类
函数名相同相同相同
参数列表不同相同任意
virtual否是否

纯虚函数与抽象类

在虚函数后加上 =0,该函数即为纯虚函数。包含纯虚函数的类称为抽象类。抽象类不能实例化对象,必须由派生类重写所有纯虚函数后才能实例化。这在一定程度上强制了接口的实现。

class Car {
public:
    virtual void Drive() = 0;
};

class Benz : public Car {
public:
    virtual void Drive() {
        cout << "Benz-舒适" << endl;
    }
};

class BMW : public Car {
public:
    virtual void Drive() {
        cout << "BMW-操控" << endl;
    }
};

int main() {
    // Car car; // 编译报错:无法实例化抽象类
    Car* pBenz = new Benz;
    pBenz->Drive();
    Car* pBMW = new BMW;
    pBMW->Drive();
    delete pBenz;
    delete pBMW;
    return 0;
}

多态的实现原理

虚函数表指针

含有虚函数的类对象中,至少包含一个虚函数表指针(vptr)。它指向该类的虚函数表(vtable)。

class Base {
public:
    virtual void Func1() { cout << "Func1()" << endl; }
protected:
    int _b = 1;
    char _ch = 'x';
};

int main() {
    Base b;
    cout << sizeof(b) << endl; // 结果通常为 12 bytes
    return 0;
}

运行结果为 12 字节,除了成员变量 _b 和 _ch,还多了一个 __vfptr(虚表指针)。该指针通常位于对象起始位置(部分平台可能在末尾)。

动态绑定与静态绑定

  • 静态绑定:不满足多态条件(如直接对象调用),在编译时确定函数地址。
  • 动态绑定:满足多态条件(指针/引用 + 虚函数),在运行时通过 vptr 查找 vtable 确定函数地址。

汇编层面可见差异:动态绑定需要间接寻址(mov eax, dword ptr[vptr]),而静态绑定直接跳转。

虚函数表的存储

基类对象的虚表中存放基类所有虚函数的地址。同类型对象共用一张虚表,不同类型对象拥有独立虚表。 派生类虚表包含:

  1. 基类虚函数地址(未被重写则保留)。
  2. 派生类重写后的虚函数地址(覆盖基类地址)。
  3. 派生类新增的虚函数地址。

虚表本质上是一个存虚函数指针的数组,通常以 0x00000000 结尾(VS 编译器常见,GCC 可能省略)。虚表和虚函数本身存储在代码段或常量区。

目录

  1. C++ 多态核心解析:虚函数重写与动态绑定原理
  2. 多态的定义及实现条件
  3. 虚函数基础
  4. 虚函数的重写
  5. 其他重写问题
  6. override 和 final 关键字
  7. 重载、重写与隐藏的区别
  8. 纯虚函数与抽象类
  9. 多态的实现原理
  10. 虚函数表指针
  11. 动态绑定与静态绑定
  12. 虚函数表的存储
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • ESP32 无人机合规识别:ArduRemoteID 开源方案详解
  • ABAP 连接 MySQL:原生限制与集成方案解析
  • Java 网络编程与网络通信基础
  • OpenClaw Skills 合集开源,收录超 1700 个本地化 AI Agent 技能
  • uv 安装多版本 Python 及虚拟环境使用指南(替代 pyenv + pip)
  • 前端流式输出实现:原理与实践
  • Spring Cloud 微服务:使用 OpenFeign 优雅实现远程调用
  • Linux Socket 编程核心:深入解析 sockaddr 数据结构族
  • Transformer 作者访谈:解析谷歌为何未率先推出 ChatGPT
  • AI 赋能原则 10 解读:政府 2.0 与公共智能系统建设
  • Transformer 位置编码原理与实现
  • Java 基础语法:标识符、数据类型与变量详解
  • 基于 YOLO 系列与 SpringBoot 的行人车辆检测系统
  • Linux 基础 IO(四):用户缓冲区深度解析
  • C 语言游戏开发:Pygame、SDL、OpenGL 深度解析
  • 基于 Amazon SageMaker 的 AIGC 模型训练与 Web 应用部署实践
  • MySQL 水平分库分表与垂直分库分表解析
  • Spring Boot Web 三大核心交互实战:表单、AJAX 与 JSON
  • 微服务监控与运维体系:构建可观测的 Java 微服务
  • Python 生成器函数深度解析:asyncio 事件循环底层实现与异步编程实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online