虚函数和 RTTI实现运行时多态

虚函数 和 RTTI 是 C++ 实现运行时多态的两大核心机制。


1. 虚函数(Virtual Function)

什么是虚函数?

在基类中用 virtual 声明的成员函数。当通过基类指针或引用 调用时,实际执行的是对象真正类型(动态类型)对应的函数版本,而不是指针/引用声明的类型(静态类型)。

核心作用

实现运行时多态(动态绑定)。

简单原理(实现层面)
  • 含有虚函数的类会生成一张 虚函数表(vtable)。
  • 每个对象会多一个隐藏的指针 vptr,指向自己类的 vtable。
  • 调用虚函数时,通过 vptr 找到正确的函数地址再执行。
示例
cpp 复制代码
#include <iostream>

class Animal {
public:
    virtual void speak() const {          // 虚函数
        std::cout << "Animal speaks\n";
    }
    virtual ~Animal() = default;          // 强烈建议虚析构
};

class Dog : public Animal {
public:
    void speak() const override {         // 重写
        std::cout << "Woof!\n";
    }
};

class Cat : public Animal {
public:
    void speak() const override {
        std::cout << "Meow!\n";
    }
};

int main() {
    Animal* a1 = new Dog();
    Animal* a2 = new Cat();

    a1->speak();   // 输出 Woof!(按动态类型调用)
    a2->speak();   // 输出 Meow!

    delete a1;
    delete a2;
}
重要规则
点 说明
虚析构函数 通过基类指针 delete 时,必须虚析构,否则只调用基类析构,导致资源泄漏
override C++11 推荐使用,明确表示重写,防止拼写错误
纯虚函数 virtual void f() = 0; 使类成为抽象类,不能实例化
非虚函数 始终按静态类型调用,不会动态分派

2. RTTI(Runtime Type Information)

什么是 RTTI?

运行时类型信息。允许程序在运行时查询对象的真实类型。

C++ 提供两个主要工具:

  1. typeid
  2. dynamic_cast

前提 :类型必须是多态类型 (至少有一个虚函数),否则 typeid 和 dynamic_cast 只能看到静态类型。

2.1 typeid

返回 const std::type_info&,可用来比较类型。

cpp 复制代码
#include <iostream>
#include <typeinfo>

Animal* p = new Dog();

if (typeid(*p) == typeid(Dog)) {
    std::cout << "这是一只狗\n";
}

std::cout << typeid(*p).name() << '\n';  // 实现定义(可能是 "3Dog" 或 "class Dog")

注意:typeid 对引用和指针解引用后的对象才有效地获取动态类型。

2.2 dynamic_cast(安全向下转型)

在运行时检查转型是否合法:

  • 成功 → 返回正确的指针/引用
  • 失败 → 指针返回 nullptr,引用抛出 std::bad_cast
cpp 复制代码
Animal* p = new Dog();

// 向下转型
if (Dog* d = dynamic_cast<Dog*>(p)) {
    d->speak();          // 安全使用
    // 可以调用 Dog 特有的成员
} else {
    std::cout << "不是 Dog\n";
}

// 引用版本(失败会抛异常)
try {
    Dog& d = dynamic_cast<Dog&>(*p);
} catch (const std::bad_cast& e) {
    std::cout << "转型失败\n";
}
dynamic_cast 的常见用途
  • 安全地从基类指针转回派生类指针
  • 跨继承体系转型(多继承时)
  • 判断对象是否属于某个类型

虚函数 vs RTTI 对比

特性 虚函数 RTTI(typeid / dynamic_cast)
目的 调用正确的函数版本 查询/检查真实类型
性能 很快(一次间接调用) 稍慢(需要查类型信息)
依赖 虚函数表 同样依赖虚函数(多态类型)
使用场景 日常多态调用 需要知道具体类型时(较少用)

使用建议

  1. 优先使用虚函数

    绝大多数多态场景用虚函数就够了,更清晰、更高效。

  2. 少用 RTTI

    频繁使用 dynamic_cast 或 typeid 通常是设计不够好的信号(可以用虚函数或 visitor 模式替代)。

  3. 必须虚析构

    只要类可能被基类指针删除,就应该把析构函数声明为虚函数。

  4. 抽象基类

    如果基类不应该被实例化,就把关键接口写成纯虚函数。


一句话总结:

  • 虚函数 → 让"调用哪个函数"在运行时决定
  • RTTI → 让"对象到底是什么类型"在运行时可知

两者都依赖"多态类型"(有虚函数),共同支撑 C++ 的运行时多态能力。

相关推荐
一个有温度的技术博主1 小时前
凌晨两点的死锁:当线程“互相等死“
开发语言·后端·场景
Είναι η κοπέλα1 小时前
llama.cpp 与 GGUF 格式:本地大模型的“裸引擎“
开发语言·人工智能·pytorch·python·conda
今夜有雨.1 小时前
图像运算、掩膜/ROI 与绘制交互
c语言·数据结构·c++·qt·算法·计算机视觉
UIU1141 小时前
P1028 [NOIP 2001 普及组] 数的计算
c++·算法
大草原的小灰灰2 小时前
Python面向对象编程
开发语言·python
知识分享小能手2 小时前
C学习教程,从入门到精通,C语言概述 —— 知识点详解(1)
c语言·开发语言·学习
垆边人似月.2 小时前
日志时间窗口内的最大并发请求数(100分 / 滑动窗口 + 排序)
数据结构·c++·算法
Chen_harmony3 小时前
一、C++入门基础
开发语言·c++
学逆向的3 小时前
远程线程注入
开发语言·jvm·网络安全·win32