cast 类型 用途 安全性 常用程度 学起来一句话总结
static_cast 编译期安全转换 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 能编译过的正常类型转换基本用它
reinterpret_cast 位级别重解释 允许你“把东西当成另一种东西看”
const_cast 去掉 / 添加 const ⭐⭐⭐ ⭐⭐⭐ 唯一可以丢掉 const 的 cast
dynamic_cast 运行时检查 RTTI 的安全向下转型 ⭐⭐⭐⭐⭐ ⭐⭐ 专为多态类型设计

1️⃣ static_cast —— 最常用、相对安全的转换

✔ 用途

  • 基本类型转换(如 int → double)

  • 有继承关系的向上转型(Child* → Parent*)

  • void* → T*(安全的 static 转)

  • 调用显式构造函数

✔ 特性

  • 编译期检查(不允许危险、不合理转换)

  • 不能去 const

  • 不能做不相关类型的指针互转(比如 int* → double*)

double d = static_cast<double>(5); // OK

Parent* p = static_cast<Parent*>(child_ptr); // 向上转换安全

void* vp = &d;
double* dp = static_cast<double*>(vp); // OK,static_cast可用于 void*

❌ 错误示例(会编译失败)

int* ip = static_cast<int*>(some_double_ptr); // 不允许,不安全

一句话总结:

能用 static_cast 的地方,就不用别的 cast,它最“正统”。

2️⃣ reinterpret_cast —— 最危险但最强的转换

✔ 用途

  • 任意指针之间互转(int* → double*)

  • 指针 → 整数(uintptr_t) → 指针

  • 将一块内存按另一种类型解释(“类型重解释”)

✔ 特性

  • 底层二进制不变,只改变解释方式

  • 没有安全检查

  • 很容易造成未定义行为(UB)

  • 多用于:

    • 底层系统编程(驱动、内核代码)

    • 内存池

    • 自己写序列化

    • 跨语言 / 跨库的 API

int i = 0x11223344;
char* p = reinterpret_cast<char*>(&i); // 获取字节级别内容

long addr = reinterpret_cast<long>(p); // 指针 <-> 整数

❌ 错误风险

double* dp = reinterpret_cast<double*>(p); 
double x = *dp; // 未定义行为!因为内存不是 double 布局

一句话总结:

reinterpret_cast 是“霸王硬上弓”,能做任何转换,但你必须保证安全性。

3️⃣ const_cast —— 唯一能修改 const 属性的转换

✔ 用途

  • 去掉 const

  • 去掉 volatile

  • 添加 const(极少用这个)

✔ 特性

  • 只能改变对象的 const / volatile 修饰,不改变类型本身

  • 必须非常小心:
    移除 const 后修改对象 → 未定义行为(UB)

✔ 示例:去掉 const

void f(const int* p) {
    int* q = const_cast<int*>(p);
    *q = 10;  // ⚠ 若 p originally 指向的是 const 对象,UB!
}

所以 const_cast 只有一个真正安全的用途:

当你知道对象原本不是 const,只是接口要求参数是 const 时。

比如 API:

void foo(const int* x) {
    int* p = const_cast<int*>(x); // 只要你保证 x 指向的不是 const 对象
}

一句话总结:

const_cast 专门用来处理“去 const”的场景,不做类型转换。

4️⃣ dynamic_cast —— 多态体系中的安全向下转型

✔ 用途

  • 子类 / 父类之间安全的类型转换

  • 只能用于 带虚函数(有 RTTI) 的类层次结构

✔ 向下转型(Parent → Child)

Parent* p = new Child();
Child* c = dynamic_cast<Child*>(p); // OK

✔ 失败时会返回 nullptr(指针版本)

Parent* p = new Parent();
Child* c = dynamic_cast<Child*>(p); // c == nullptr,安全!

✔ 运行时检查(RTTI)

  • 会查对象的实际类型

  • 是唯一一个带“运行时成本”的 cast

✔ 引用版本失败会抛异常

Parent& p = Parent();
Child& c = dynamic_cast<Child&>(p); // throws std::bad_cast

一句话总结:

dynamic_cast 是唯一“真正安全”的向下转型方式,用于多态。

重点区分static_cast和reinterpret_cast

🟦 static_cast —— 语义转换,真正变成新的类型

✔ static_cast 会做“逻辑正确的”类型转换

  • int → double(改变值,类型变了)

  • Derived* → Base*(安全的向上转型)

  • void* → T*(但你保证原本就是 T)

✔ static_cast 会让变量成为目标类型(有意义的转换)

例如:

int i = 42;
double d = static_cast<double>(i);  // d == 42.0

这里:

  • 内存重新组织(int → double)

  • 表示的数据类型改变

  • 语义上是新值

再比如:

Derived* d;
Base* b = static_cast<Base*>(d);

这里:

  • 类型系统认可

  • 行为可预测

  • 对象仍然属于 Derived,只是你看它“是个 Base 指针”

static_cast 永远不会破坏对象布局,是安全的语义转换。

🟥 reinterpret_cast —— 不改变内存,只改变解释方式

✔ 它不会“改变类型”,只是“改变阅读器”

想象你拿一本书(内存):

  • static_cast:把内容按照需求重新排版成新书

  • reinterpret_cast:不改内容,换一副不同的眼镜(把文字当成别的语言看)

int x = 0x11223344;
char* p = reinterpret_cast<char*>(&x);

内存布局:

44 33 22 11   (在小端序机器上)

你只是 用 char 的方式读它的内容*,而不是把 int 转成 char。

再看危险例子:

int* ip = new int(10);
double* dp = reinterpret_cast<double*>(ip);
double d = *dp;  // ❌ 未定义行为

原因:

  • 你告诉编译器:“这块 int 的内存,是 double!”

  • 但布局完全不对,因此读取是错误的

✔ reinterpret_cast 的本质:

内存原样不动,只改变你访问它时使用的类型。

🧠 二者区别可以用一句话记住:

static_cast:转换 = 合理的,编译器帮你确保安全

reinterpret_cast:转换 = 你说了算,后果自负

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐