C++ cast 全解
更新时间:2026-08-27。本文是
languages/cpp/主题高手层第 17 篇。C 语言里一个(int)x走天下,类型转换"能编过就行"。C++ 把类型转换拆成四种,各管一摊——有的编译期检查、有的运行时验证、有的故意不做检查。用对转换,是类型安全的最后一道防线;用错转换,是未定义行为的快速通道。
本文要回答的问题
- 四种 cast 分别解决什么问题?语法和适用场景是什么?
dynamic_cast什么时候返回nullptr?什么时候抛异常?const_cast去掉 const 之后修改对象合法吗?- 为什么说 C 风格转换是"不安全的万能转换"?
四种 cast 一览
| cast | 时机 | 检查 | 用途 |
|---|---|---|---|
static_cast | 编译期 | 静态类型检查 | 大多数普通转换 |
dynamic_cast | 运行时 | 动态类型检查 | 多态类型安全向下转换 |
const_cast | 编译期 | 无 | 增删 const/volatile |
reinterpret_cast | 编译期 | 无 | 位级重解释(指针/整数互转) |

static_cast:最常用的转换
编译期完成,编译器做静态类型检查。用于:
// 数值转换
double d = 3.7;
int n = static_cast<int>(d); // 3,截断
// 基类指针 → 派生类指针(向下转换,无检查)
class Base {};
class Derived : public Base {};
Base *b = new Derived;
Derived *p = static_cast<Derived*>(b); // 编过,但如果是纯 Base 对象就 UB
// void* → 具体指针
void *raw = &n;
int *pi = static_cast<int*>(raw);static_cast 做向下转换不做检查——它相信程序员。如果对象实际上不是 Derived,行为未定义。安全的下转用 dynamic_cast。
dynamic_cast:运行时检查的下转
dynamic_cast 在运行时检查对象真实的动态类型,多态(有虚函数)场景下安全:
class Animal { virtual void speak() {} };
class Dog : public Animal {};
class Cat : public Animal {};
void make_speak(Animal *a) {
if (Dog *d = dynamic_cast<Dog*>(a)) {
d->speak(); // 是 Dog 才进来
} else {
// 不是 Dog
}
}| dynamic_cast 形式 | 失败表现 |
|---|---|
指针:dynamic_cast<T*>(p) | 返回 nullptr |
引用:dynamic_cast<T&>(r) | 抛 std::bad_cast 异常 |
Animal *a = new Cat;
Dog *d = dynamic_cast<Dog*>(a); // nullptr(a 实际是 Cat)
try {
Dog &dr = dynamic_cast<Dog&>(*a); // 抛 std::bad_cast
} catch (const std::bad_cast &e) { /* ... */ }代价:依赖 RTTI(运行时类型信息),有性能开销(运行时查类型)。工程建议:尽量用虚函数 + 多态代替 dynamic_cast 的分支逻辑,dynamic_cast 只在"跨继承体系判断类型"时用。
const_cast:去常量的"非常手段"
const_cast 增删 const/volatile 限定,不做任何类型转换:
void legacy_api(char *buf); // 旧接口要 char*,但函数内部不改
void process(const std::string &s) {
legacy_api(const_cast<char*>(s.c_str())); // 去掉 const
}大坑:去掉 const 后修改原本是 const 的对象 = 未定义行为!
const int x = 5;
const_cast<int&>(x) = 10; // UB!x 本来是 const
// 但:
int y = 5;
const int &cy = y;
const_cast<int&>(cy) = 10; // OK!y 本身不是 const,只是引用带 const| 场景 | 是否安全 |
|---|---|
| 对象本身是 const | UB(即使编译通过) |
| 对象本身非 const,仅引用/指针带 const | 安全 |
经验:const_cast 是兼容旧 C 接口的"必要之恶",正常代码不应该出现。 出现 const_cast 往往意味着设计有问题(谁该负责 const 应该更明确)。
reinterpret_cast:位级重解释
不做任何检查,直接把位的含义重解释。最危险的一种:
// 整数 ↔ 指针(平台相关)
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
SomeType *p = reinterpret_cast<SomeType*>(0x1234);
// 类型不相关指针互转
int i = 1;
float *fp = reinterpret_cast<float*>(&i); // 把 int 当 float 读,UB规则:reinterpret_cast 的结果几乎总是实现定义或未定义行为,只在系统编程(寄存器地址、序列化底层)等明确场景用,普通业务代码禁用。
为什么不用 C 风格转换
int x = 42;
double d = (double)x; // C 风格
double d2 = static_cast<double>(x); // C++ 风格
const int *cp = &x;
int *p = (int*)cp; // C 风格:const_cast + static_cast 混合
int *p2 = const_cast<int*>(cp); // C++:意图明确C 风格转换的问题是**"一把抓"**:它尝试 static_cast、const_cast、reinterpret_cast 的组合,具体走了哪条路径不透明。看代码的人无法知道这个转换是"安全类型转换"还是"危险的位级重解释"。
| 对比项 | C 风格转换 | C++ 四种 cast |
|---|---|---|
| 意图表达 | 模糊 | 每种意图对应一种 |
| 安全检查 | 无 | static_cast 编译期查、dynamic_cast 运行时查 |
| 可搜索性 | 难(到处是括号) | 易(关键词可 grep) |
| 推荐度 | 尽量避免 | 按需选型 |
工程规范:代码库统一禁止 C 风格转换(可用编译选项或 linter 强制),强制写四种 cast 之一。 这让类型转换的意图一目了然,也便于代码审查。
与本站性能主线衔接
- 性能对比:
static_cast零开销(编译期)、dynamic_cast有 RTTI 运行时开销、reinterpret_cast零开销(纯位操作),热路径选型时要知道差异。 - 多态安全:
dynamic_cast与 虚函数与多态 的 RTTI 机制相关。 - 系统编程:
reinterpret_cast在设备寄存器访问中出现,衔接 硬件与系统。 - 性能剖析:滥用 dynamic_cast 会在火焰图出现 RTTI 热点,用 perf 定位。
一句话总结
四种 cast 各管一摊:static_cast 编译期常规转换、dynamic_cast 运行时安全下转(失败返 nullptr/抛 bad_cast)、const_cast 增删 const(对真 const 对象修改是 UB)、reinterpret_cast 位级重解释(仅系统编程);业务代码禁用 C 风格转换,用四种 cast 表达意图。