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

【C++】C++ 中多态是什么?咋用的?

在 C++ 中,“多态”(Polymorphism)是个你早晚会碰到的概念。
它是“同一接口,不同实现”这一思想的落地方案。
而且——它不仅仅是语法糖,背后还有一套完整的运行机制和性能权衡。

今天我们讲多态~


1. 多态的基本概念

在 C++ 中,多态主要分为两类:

  1. 编译时多态(静态多态)

    • 发生在编译阶段,比如函数重载(overloading)和模板(templates)。
    • 编译器在编译期间就能决定调用哪一个函数版本,没有运行时开销。

    所以之前我们讲到的模板和重载等知识,其实就是多态下的一种类型~

  2. 运行时多态(动态多态)

    • 通过继承 + 虚函数(virtual function)实现。
    • 具体调用哪个函数,要等到程序运行的时候才能确定(依赖虚函数表)。

继承和虚函数的概念前面也讲过了,因为有一个具体查表的过程,所以被列为动态的多态。

今天我们主要聊第二种——因为这才是大多数人提到“多态”时的意思。


2. 运行时多态是怎么工作的?

先看一个例子:

#include <iostream>
using namespace std;class Instrument {
public:virtual void play() {cout << "Playing some instrument" << endl;}
};class Guitar : public Instrument {
public:void play() override {cout << "Playing guitar" << endl;}
};class Piano : public Instrument {
public:void play() override {cout << "Playing piano" << endl;}
};int main() {Instrument* i1 = new Guitar();Instrument* i2 = new Piano();i1->play();  // 输出:Playing guitari2->play();  // 输出:Playing pianodelete i1;delete i2;
}

我们看这个例子,

  • virtualplay() 成为虚函数。 当你用基类指针指向派生类对象时,调用 play() 会根据对象的实际类型来决定执行哪个版本。
  • 底层原理是什么呢?底层原理是虚函数表(vtable) :每个含有虚函数的类都会有一张函数指针表,指向实际要执行的函数。接下来具体讲解下这个表是怎么个事。

3.虚函数表

想象一下,有这样一个类:

class Base {
public:virtual void func1();virtual void func2();
};

当编译器发现类中有虚函数时,它会:

  1. 为类生成一张虚函数表(vtable):本质上是一个函数指针数组,存放着当前类的虚函数入口地址。
  2. 在每个对象中,悄悄插入一个指针(vptr) ,指向该类的虚函数表。

如果有派生类覆盖了虚函数,比如:

class Derived : public Base {
public:void func1() override; // 覆盖 func1
};

那么:

  • Derived 的 vtable 里,func1 会指向新实现的版本;
  • func2 会继续沿用 Base 的实现。

调用过程:

Base* obj = new Derived();
obj->func1();

执行时:

  1. 通过 obj 找到对象的 vptr
  2. 根据 vptr 定位到 Derived 的 vtable;
  3. 找到 func1 在表中的位置,调用对应的函数地址。

这样,就实现了“同一个函数调用,在不同对象上执行不同的代码”——这就是运行时多态。

所以怎么说呢,虚函数表就相当于是给定了指针,那么就直接通过地址来找你的具体实现方法,这样就可以避免函数冗余等问题,这也是多态的核心表。

那为啥要用这种机制呢?


4. 为什么要用这种机制?

C++ 是强类型、静态编译的语言,如果没有虚函数表机制,所有函数调用的目标地址在编译时就已经确定,根本没法在运行时根据对象类型切换行为。

虚函数表的意义就是:

  • 让编译器能在运行时,根据对象的实际类型,动态选择函数实现;
  • 同时保持语法上的统一(基类指针/引用就能调用不同对象的实现)。

5. 戳细节!

一旦理解了 vtable,就能顺理成章地理解多态相关的几个常见知识点:

5.1 基类析构函数必须是虚函数

如果析构函数不是 virtualdelete 基类指针时只会调用基类析构,而不会调用派生类析构,导致资源泄漏。

因为析构过程也是通过 vtable 调用的,缺少虚析构会让派生类的析构函数没有入口。


5.2 虚函数有运行时开销

每次调用虚函数都需要有以下步骤:

  1. 访问对象的 vptr(一次内存读取)
  2. 访问 vtable(一次内存读取)
  3. 再跳转到目标函数(一次间接跳转)

这会让虚函数的调用性能比普通函数略差,且不能内联(inline)。有点繁琐是难免的()


5.3 多继承与菱形继承

  • 多继承:如果一个类有多个虚函数表(因为继承多个含虚函数的基类),对象会有多个 vptr
  • 虚拟继承:编译器会调整 vtable 的布局,确保共享的基类只有一份实例。

这些机制都会增加对象布局的复杂度和内存占用。也就是我们上面说的会有函数冗余,菱形继承也是冗余的一种。


5.4 纯虚函数与接口抽象

class Shape {
public:virtual void draw() = 0; // 纯虚函数
};

纯虚函数的类不能直接实例化,只能作为接口存在。
这是一种典型的多态应用模式:面向接口编程


6. 总结~

多态的本质(这里主要是运行时多态):

基于虚函数表和 vptr,通过运行时查表来动态决定调用哪个函数版本。

理解多态是理解C++中函数复用以及不同场景不同方法的基础,这其实也是我们现实生活中很多物品的分类方法~由大到小嘛

http://www.dtcms.com/a/438274.html

相关文章:

  • Dijkstra最短路径算法
  • 扶沟县网站开发怎么知道一个网站是哪家公司做的
  • mysql基于GTID恢复数据
  • LabVIEW正弦波去噪与信号处理
  • winform 五子棋
  • Java实现BCH与BTC的地址相互转换
  • CyberSecurity:SSL Client-Initiated Renegotiation 客户端发起的重新协商ddos攻击
  • 关于Mybatis-Plus的insertOrUpdate()方法使用时的问题与解决—数值精度转化问题
  • 石家庄做网站制作公司网站写作赚钱
  • Docker、容器、虚拟机到底是什么
  • 西安微信商城网站开发做阿里巴巴怎么进公司网站
  • Go语言入门(17)-接口
  • WebPages 安全
  • 安卓基础组件030-进程和线程
  • [ vue 前端框架 ] 基本用法和vue.cli脚手架搭建
  • 鸿蒙Next中使用Socket进行网络通信:完整指南与实战
  • dw如何做商业网站淘宝网站建设流程
  • 网站做好了 怎么做解析wordpress 怎么迁移
  • GNS3 3.0.5新版教程,以及Cloud设备找不到VMware网卡的解决方法
  • 比奇堡合唱团制作教学,AI翻唱教学动漫角色歌曲
  • 活动展板设计:大尺寸 + 高分辨率,打印清晰
  • 深圳市城乡建设部网站首页一个网站如何做盈利
  • 【IMX6ULL驱动学习】I2C驱动
  • 基于物联网数据采集的大型应用程序软件架构设计:核心要点、结构内容与链路关系
  • 【连载5】云数据库 MySQL 热点更新功能介绍
  • (四)Webpack、Slot与Vue CLI脚手架
  • 【附源码】个人事务管理系统的设计与实现
  • 基于PCIe(XDMA)的多路(1-32路)信号采集与回放子系统,多路视频、AD、光纤等信号,支持PR over PCIe
  • 【STM32项目开源】基于STM32的智能电子秤
  • 网站建设专用图形库西安网站快速排名提升