C++中通过虚函数实现多态的原理

embedded/2025/3/15 6:46:08/

C++中通过虚函数实现多态的原理

我们都知道C++是通过虚函数实现多态的,那么其中的原理是什么呢?

在C++中,多态性是一种重要的特性,它允许通过基类指针或引用来调用派生类中的函数。多态性主要分为两种:编译时多态(主要通过函数重载和模板实现)和运行时多态(主要通过虚函数实现)。C++中通过虚函数实现的多态主要是运行时多态(动态多态),其原理主要基于虚函数表(vtable)和虚函数指针(vptr)。

1. 虚函数的定义

虚函数是在基类中使用virtual关键字声明的成员函数。例如:

class Base 
{
public:virtual void show() {cout << "Base::show" << endl;}
};

当一个类的成员函数被声明为虚函数后,C++运行时系统会为该类及其派生类提供动态绑定的机制。

2. 虚函数表(vtable)

虚函数表是一个由编译器生成的数组,其中存储了类中所有虚函数的地址。每个包含虚函数的类都有一个唯一的虚函数表。例如:

class Base 
{
public:virtual void show() {cout << "Base::show" << endl;}virtual void display() {cout << "Base::display" << endl;}
};

对于Base类,编译器会为其生成一个虚函数表,其中包含showdisplay的地址。

3. 虚函数指针(vptr)

每个包含虚函数的类的对象都会有一个隐藏的成员变量——虚函数指针(vptr)。这个指针指向该对象所属类的虚函数表。当对象被创建时,构造函数会初始化这个指针,使其指向正确的虚函数表。

4. 多态的实现原理

当通过基类指针或引用调用虚函数时,C++运行时系统会通过对象的vptr找到对应的vtable,然后通过vtable找到正确的虚函数地址并调用。这个过程称为动态绑定或晚期绑定。

#include <iostream>
using namespace std;class Base {
public:virtual void show() {cout << "Base::show" << endl;}
};class Derived : public Base {
public:void show() override {cout << "Derived::show" << endl;}
};int main() {Base* ptr;Base base;Derived derived;ptr = &base;ptr->show();  // 输出:Base::showptr = &derived;ptr->show();  // 输出:Derived::showreturn 0;
}

解释:

  1. Base base对象:
    • base对象的vptr指向Base类的vtable。
    • Base类的vtable中存储了Base::show的地址。
    • 当调用ptr->show()时,运行时通过ptr对象的vptr找到Base类的vtable,并调用Base::show
  2. Derived derived对象:
    • derived对象的vptr指向Derived类的vtable。
    • Derived类的vtable中存储了Derived::show的地址(覆盖了Base::show)。
    • 当调用ptr->show()时,运行时通过ptr对象的vptr找到Derived类的vtable,并调用Derived::show

5. 虚函数表的存储结构

假设Base类和Derived类的虚函数表存储如下:

  • Base类的vtable:
    • Base::show
    • Base::display(如果有其他虚函数)
  • Derived类的vtable:
    • Derived::show(覆盖了Base::show
    • Base::display(如果没有覆盖,仍然指向基类的虚函数)

6. 多态的条件

要实现通过虚函数的多态,必须满足以下条件:

  1. 继承关系: 派生类继承自基类。
  2. 虚函数: 基类中必须有虚函数。
  3. 覆盖: 派生类覆盖了基类的虚函数。
  4. 使用基类指针或引用: 通过基类指针或引用调用虚函数。

7. 总结

通过虚函数实现多态的核心是虚函数表(vtable)和虚函数指针(vptr)。vtable存储了类中虚函数的地址,vptr指向对象所属类的vtable。运行时通过vptr找到正确的vtable,再通过vtable找到正确的虚函数地址并调用,从而实现动态绑定。

这种机制使得程序能够在运行时根据对象的实际类型调用对应的虚函数,而不是根据指针或引用的类型,从而实现了多态。


http://www.ppmy.cn/embedded/172694.html

相关文章

代码随想录 回溯

131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 这题挺难的&#xff0c;搞了两个小时才一知半解吧qaq 思路&#xff1a;首先要明白什么作为终止条件&#xff0c;其次就是for循环内什么时候插入path&#xff0c;剩下的就是套模板了&#xff0c;其次补充一下回文数的…

996引擎-自定义属性:配表 + 映射

996引擎-自定义属性:配表 + 映射 自定义属性ID范围200-399配属性表Mir200中做映射放到测试装备上编辑 tips 显示对自定义属性实现对原属性的加成效果,这个方案是最简单的。 如果要加更多的业务逻辑,则需要代码实现。 自定义属性ID范围200-399 配属性表 Envir\Data\cfg_att…

Netty基础—4.NIO的使用简介二

大纲 1.Buffer缓冲区 2.Channel通道 3.BIO编程 4.伪异步IO编程 5.改造程序以支持长连接 6.NIO三大核心组件 7.NIO服务端的创建流程 8.NIO客户端的创建流程 9.NIO优点总结 10.NIO问题总结 4.伪异步IO编程 (1)BIO的主要问题 (2)BIO编程模型的改进 (3)伪异步IO编程 …

智能制造:构筑网络新安全“智”造

在这个日新月异的数字化、网络化、智能化时代&#xff0c;信息安全不仅仅是一个简单的技术问题&#xff0c;更是企业业务管理的重要组成部分。对于智能制造企业而言&#xff0c;信息安全关乎的不仅是系统的稳定运行和数据的安全保护&#xff0c;更直接影响到企业的生产效率、运…

@Async 注解不生效的常见原因及解决方案

Async 注解不生效的常见原因及解决方案 1. 未启用异步支持 原因&#xff1a;Spring 默认不开启异步处理&#xff0c;需手动启用。 解决&#xff1a;在配置类上添加 EnableAsync 注解。 Configuration EnableAsync // 关键注解 public class AsyncConfig { }2. 方法调用方式错…

Unity Webgl在编辑器中报错:Cannot connect to destination host

问题描述&#xff1a; 前段时间开发的webgl项目突发异常&#xff0c;打包出来的webgl可以正常登录&#xff0c;而在unity编辑器中登录的时候无法进入&#xff0c;控制台报出&#xff1a;Cannot connect to destination host&#xff1b; 解决办法&#xff1a; 就很神奇的一件…

LeetCode 解题思路 16(Hot 100)

解题思路&#xff1a; 初始化辅助节点&#xff1a; dummy&#xff1a;哑节点。pre&#xff1a;当前链表的前一个节点。start&#xff1a;当前链表的第一个节点。end&#xff1a;当前链表的最后一个节点。nextStart&#xff1a;end.next&#xff0c;下组链表的第一个节点&…

HashMap的奇幻漂流:当一个数组决定去整容

标准答案&#xff08;面试官最爱版&#xff09; HashMap实现原理&#xff1a; 数据结构&#xff1a;数组链表/红黑树&#xff08;Java 8&#xff09;哈希算法&#xff1a;(h key.hashCode()) ^ (h >>> 16)索引计算&#xff1a;(n-1) & hash&#xff08;n为数组…