原来 C++ 这么简单:每日十题轻松学 (Day 4 多态与 RTTI 篇)

📘 本篇围绕 C++ 运行时多态机制(虚函数、虚表、类型识别等)展开。虚函数是类设计的灵魂,但也隐藏许多坑:对象切割、析构不虚、类型转换失败……本篇精选 10 个实际常见的问题,解释 + 示例 + 延伸练习,助你培基托底,深入内核。


🔁 Day 3 快速回顾

矩点记忆要点
构造函数类型默认、有参、拷贝、移动
explicit禁止隐式类型转换
Rule of Five自编辑资源管理类必须遵守
静态成员变量类共用,非对象的成员
对象大小受成员顺序、对齐和虚表影响

🔹 今日目标:进入 C++ 运行时多态的核心机制

  • 明确虚函数、vtable 和 vptr 的本质
  • 理解不同条件下多态是否生效
  • 识别对象切割、析构不虚等隐怪问题
  • 熟悉二连符工具:typeiddynamic_cast

🎓 十大核心问题 + 深层解读

【Q1】什么是虚函数?

📅 概念:
虚函数用 virtual 关键字声明,支持运行时多态

🔎 代码示例:

class Animal {
public:
    virtual void speak() { std::cout << "Animal\n"; }
};
class Dog : public Animal {
public:
    void speak() override { std::cout << "Dog\n"; }
};

🔹 区别:

类型绑定时机是否支持多态
普通函数编译期不支持
虚函数运行期支持

【Q2】虚函数的本质是什么?

📅 原理:
C++ 通过“虚函数表 vtable ” 和 “虚表指针 vptr ” 实现多态

🔎 代码示意:

  • 每个有虚函数的类都有一个 vtable
  • 每个对象内部隐藏一个 vptr 指向对应的 vtable

🔹 调用虚函数 = 通过 vptr 查表调用


【Q3】多态生效需要哪些条件?

🔹 有效多态的 3 大条件:

  1. 基类函数是 virtual
  2. 子类重写了此虚函数
  3. 通过“基类指针/引用” 调用

🔔 错误示例:

Dog d;
d.speak(); // 非多态,编译期绑定

🔔 正确示例:

Animal* a = new Dog;
a->speak(); // 多态,运行期确定调用

【Q4】什么是对象切割 (Object Slicing) ?

📅 定义: 用子类对象输入基类对象,就会被“切割成基类部分”
在这里插入图片描述

🔎 代码示例:

void print(Animal a) {
    a.speak();  // 只有 Animal 部分,切割了
}

Cat c;
print(c);  // 调用 Animal::speak()

📈 改进:
给入参改为引用/指针,防止切割


【Q5】析构函数为什么一定要声明为 virtual?

📅 问题: 如果某类有 virtual 函数,应该把析构函数也声明为 virtual

否则后果: 通过基类指针 delete 子类对象时,只调用基类析构,子类资源泄露

解決:

virtual ~Animal(); // 完全析构

【Q6】什么是 RTTI?

RTTI = Run-Time Type Information

在运行期获取对象实际类型,仅适用于含虚函数的类

两个工具操作符:

  • typeid(expr) :获取类型对象
  • dynamic_cast<T*> :安全地转换指针

【Q7】怎么用 typeid 确认类型?

Base* p = new Derived;

if (typeid(*p) == typeid(Derived)) {
    std::cout << "It's Derived\n";
}

前提: 类中有虚函数,否则 typeid(*p) 只看指针类型


【Q8】 dynamic_cast 是怎么做安全类型转换的?

Base* b = new Derived;

if (Derived* d = dynamic_cast<Derived*>(b)) {
    d->draw();  // 转换成功
} else {
    std::cout << "Conversion failed\n";
}

特性:

  • 成功返回子类指针
  • 失败返回 nullptr
  • 仅适用于含 virtual 函数的类

【Q9】含虚函数类会增加对象大小吗?

代码比较:

class A { int x; };
class B { virtual void f(); int x; };

std::cout << sizeof(A) << " " << sizeof(B);

结果: B 比 A 大,因为多了 vptr


【Q10】如何避免对象切割并保留多态?

错误示例:

Base b = Derived(); // 切割只保留基类
b.virtualFunc();    // 调用的是 Base 版本

正确使用:
使用基类指针或引用,不用值传递


📙 结论手册:重点全记

矩点记忆讲解
虚函数运行时绑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值