
从C的角度接触到dynamic_cast
dynamic_cast关键字
写这篇文章的原因是因为笔者在遇到dynamic_cast时在网上查询文章资料讲解的都不是很清楚,至少对于笔者这种没有系统学习C++的人来说,由于之前一直是使用C语言开发,所以很多思想停留在C阶段,所以笔者在看了其他文章后通过与AI沟通逐渐清晰了dynamic_cast,并写下这篇文章希望对于没有系统接触过C++(东学一下西学一下😂)的伙伴们有所帮助
笔者的知识背景:在C语言中,对于一个确定类型的指针的使用基本上都是指向一个确定的变量实体,如果需要强制类型转换需要分析两个类型的内存空间是否相同,以及强制类型转换需要额外注意野指针的出现,通常使用的都是
void *指向或者是char *指向,而对于为什么要用一个类型(非void)指针去指向另一个类型指针显得很不理解。
dynamic_cast 是 C++ 的运行时安全类型转换,只适用于带有虚函数的类层次结构(即多态类型),并且会 在运行时检查对象的实际类型是否真的可以转换。
dynamic_cast应用场景
✅ 场景:安全地“向下转型”(Downcasting)
当有一个 基类指针/引用,但实际指向的是某个派生类对象,想把它转回派生类类型,以便调用派生类特有的成员函数
xxxxxxxxxxclass Base {public: virtual ~Base() {} // 必须有虚函数(通常是虚析构函数)};class Derived : public Base {public: void specialFunction() { cout << "I'm Derived!" << endl; }};Base* ptr = new Derived; // 基类指针,实际指向 Derived 对象// 安全地尝试转成 Derived*Derived* d = dynamic_cast<Derived*>(ptr);if (d) { d->specialFunction(); // 安全调用} else { cout << "Not a Derived object!" << endl;}🔑 关键点:dynamic_cast 会在 运行时检查 ptr 指向的对象是否真的是 Derived(或其子类)。如果不是,返回 nullptr(对指针)或抛出 std::bad_cast(对引用)。
❓所提出的问题本质是:
“既然我知道要操作的是
Derived对象,为什么不直接用Derived*?为什么先用Base*指向它,再费劲用dynamic_cast转回来?这不是多此一举吗?甚至用void*不也行?”
这个问题问得非常好!如果在写代码时 100% 确定对象类型,确实不需要 dynamic_cast。
但现实中的程序往往不知道具体类型——而这正是 dynamic_cast 存在的意义。
✅ 为什么用基类指针指向派生类对象?
这是 多态的经典用法,目的是 “统一接口,不同实现”。
🌰 举个实际例子:
假设写一个图形绘制系统:
xxxxxxxxxxclass Shape {public: virtual ~Shape() = default; virtual void draw() = 0; // 纯虚函数};class Circle : public Shape {public: void draw() override { cout << "Drawing a circle\n"; } void setRadius(double r) { radius = r; } // Circle 特有方法private: double radius;};class Rectangle : public Shape {public: void draw() override { cout << "Drawing a rectangle\n"; } void setWidth(double w) { width = w; } // Rectangle 特有方法private: double width;};现在把所有图形存到一个容器里统一管理:
xxxxxxxxxxstd::vector<Shape*> shapes;shapes.push_back(new Circle);shapes.push_back(new Rectangle);👉 只能用 Shape*(基类指针)来存储它们,因为 vector 只能存同一种类型!
当遍历绘制时:
xxxxxxxxxxfor (auto shape : shapes) { shape->draw(); // 多态:自动调用 Circle::draw 或 Rectangle::draw}✅ 这时,并不知道 每个 shape 具体是 Circle 还是 Rectangle。
❓但万一想调用派生类特有方法呢?比如给某个 Circle 设置半径?
这时候你就需要 从 Shape* 转回 Circle*:
xxxxxxxxxxShape* s = /* 从某个地方拿到的,不知道具体类型 */;Circle* c = dynamic_cast<Circle*>(s);if (c) { c->setRadius(5.0); // 安全!只有真是 Circle 才调用}🔑 关键点:拿到这个指针的时候,已经“丢失”了具体类型信息,只知道它是 Shape。
✅ 什么时候你会“不知道类型”?
这在以下场景非常常见:
| 场景 | 说明 |
|---|---|
| 容器统一存储 | 如上例,vector<Base*>存多种派生类 |
| 回调函数/事件系统 | 回调传入的是Base*,但具体类型由触发方决定 |
| 插件系统 | 主程序只认识接口Base,插件提供Derived1/2/3 |
| 反序列化/工厂模式 | 从文件或网络加载对象,只知道基类类型 |
| GUI 框架 | 所有控件继承自Widget,但实际是Button、TextBox等 |
在这些场景中,代码的编写者在编译时根本不知道运行时会拿到什么具体类型