C++ 四种类型转换
🚀 C++四种类型转换详解:static_cast、dynamic_cast、const_cast、reinterpret_cast
📅 更新时间:2025年8月13日
🏷️ 标签:C++ | 现代C++ | 类型系统 | RTTI | 类型转换
文章目录
- 📖 前言
- 🔍 一、核心概念与对比
- 📝 二、static_cast
- 1.static_cast正确用法
- 2.static_cast错误用法
- 📝 三、dynamic_cast
- 1.dynamic_cast 正确用法
- 2.dynamic_cast 错误用法
- 📝 四、const_cast
- 1.const_cast 正确用法
- 2.const_cast 错误用法
- 📝 五、reinterpret_cast
- 1.reinterpret_cast 正确用法
- 2.reinterpret_cast 错误用法
- ✅ 速记
- 🧭 总结与选型建议
📖 前言
类型转换是 C++ 类型系统的锋利工具。用得好,代码安全又优雅;用不好,未定义行为与线上事故分分钟找上门。本文系统梳理 C++ 四种显式类型转换的语义边界、使用场景与陷阱,并配以可运行示例与纠错建议。
用对转换“刀法”,用最小的权限做正确的事。
🔍 一、核心概念与对比
-
static_cast
: 编译期 语义转换,适合数值/枚举/指针的“已知安全”变换与上行/受控下行。 -
dynamic_cast
:运行期 带 RTTI 检查的安全下行/交叉转换,失败返回nullptr
/抛异常。 -
const_cast
:仅移除/添加顶层const/volatile
限定,不做对象重解释。 -
reinterpret_cast
:位级重解释,几乎不受约束,但最危险,谨慎用在极少数底层场景。
优先选“更安全”的转换:dynamic_cast > static_cast > const_cast > reinterpret_cast(非严格可替换关系)。 -
C++ 强制类型转换运算符的用法如下:
强制类型转换运算符 <要转换到的类型> (待转换的表达式)
例如:
double d = static_cast <double> (3*5); //将 3*5 的值转换成实数
下面分别介绍四种强制类型转换运算符
📝 二、static_cast
static_cast
用于进行比较自然和低风险的转换,如整型和浮点型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符 T
(如 T 是 int、int* 或其他类型名),则 static_cast
也能用来进行对象到 T
类型的转换。
static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换 因为这些属于风险比较高的转换 !!!
1.static_cast正确用法
下面是会用到static_cast
的地方
- 数值显示转换
double t=3.14;
int n=t;//隐式转换 不便于排查
int n=static_cast<int>(t);//显示转换 此时n=3
- 选择函数重载
class A
{
public:operator int(){return 1;}operator double(){return 3.14;}
};int main()
{A n;int x=n;//x=1 隐式转换double x=n;//x=3.14 隐式转换int x=static_cast<int>(n);//x=1 显式转换int x=static_cast<double>(n);//x=3.14 显式转换return 0;
}
同时提供多个转换运算符在某些表达式中可能引发二义性,此时用 static_cast<目标类型>(n) 明确指定类型更便于阅读
- 枚举与整数互转(值域需自查)
enum class Color { Red = 1, Green = 2 };
int v = static_cast<int>(Color::Red); // 1
Color c = static_cast<Color>(v); // 前提:v 落在有效枚举值范围
- 继承层次的上行转换(派生 → 基类,安全;指针/引用均可)
struct Base { virtual ~Base() = default; };
struct Derived : Base {};Derived d;
Base* pb = static_cast<Base*>(&d); // OK:上行
Base& rb = static_cast<Base&>(d); // OK:上行
// 下行若不确定真实类型,请使用 dynamic_cast 以避免未定义行为
2.static_cast错误用法
- 不同类型的指针无法转换
int n = 1;
int* p = &n;
char* x = static_cast<char*>(p);//错误 不同类型指针无法转换
- 指针与整型之间的转换
int n = 1;
int* p = static_cast<int*>(n);//错误---int a = 1;
int* p = &a;
int n = static_cast<int>(p);//错误整型与指针类型之间无法转换
- 多态下行转换使用
static_cast
(风险高,可能未定义行为)
struct Base { virtual ~Base() = default; };
struct Derived : Base {};Base* pb = new Base; // 实际不是 Derived
Derived* pd = static_cast<Derived*>(pb); // 错误做法:类型不对将导致 UB
// 正确:使用 dynamic_cast<Derived*>(pb) 并判断是否为 nullptr
进一步说明:
- 上行(派生→基类)总是安全且可隐式;下行(基类→派生)只有当对象真实类型就是该派生类时才安全。
static_cast
下行不做运行期检查,类型不符会产生未定义行为(UB)。dynamic_cast
需要基类含有虚函数,指针失败时返回nullptr
,引用失败时抛出std::bad_cast
。
什么时候用哪个?
- 已经能“证明”对象真实类型是
Derived
(例如对象就是Derived
构造的):可用static_cast
下行。 - 无法确定真实类型或来自多态接口:使用
dynamic_cast
并检查结果。
示例:已知安全的下行(来源明确是 Derived)
Derived d;
Base* pb_ok = &d; // 上行
Derived* pd_ok = static_cast<Derived*>(pb_ok); // 下行:安全
示例:运行期检查的下行(不确定来源)
Base b;
Base* pb_unknown = &b; // 真实类型不是 Derived
if (auto pd2 = dynamic_cast<Derived*>(pb_unknown)) {// ... 使用 pd2
} else {// 不是 Derived,避免 UB
}
📝 三、dynamic_cast
dynamic_cast
用于多态类型间的安全下行/交叉转换,依赖 RTTI(运行期类型信息)。
dynamic_cast
是通过“运行时类型检查”来保证安全性的
1.dynamic_cast 正确用法
- 多态下行:指针转换后判空
struct Base { virtual ~Base() = default; };
struct Derived : Base { void foo() {} };void use(Base* pb) {if (auto pd = dynamic_cast<Derived*>(pb)) {pd->foo(); // 成功:pb 真实指向 Derived} else {// 失败:pb 不是 Derived,避免未定义行为}
}int main() {Derived d; Base b;use(&d); // 成功路径use(&b); // 失败路径(pd 为 nullptr)
}
注意:
- 基类需含有虚函数(开启 RTTI);否则编译不通过。
- 指针转换失败返回
nullptr
;引用失败会抛std::bad_cast
。 - 仅用于多态体系内的安全下行/交叉转换。
2.dynamic_cast 错误用法
- 用在非多态基类(没有任何虚函数)
struct Base {}; // 非多态
struct Derived : Base {};
Base* pb = new Base;
auto pd = dynamic_cast<Derived*>(pb); // 错:Base 非多态,编译失败
- 引用下行失败会抛异常(需显式处理)
struct Base { virtual ~Base() = default; };
struct Derived : Base {};
Base b;
try {Derived& rd = dynamic_cast<Derived&>(b); // 失败 => 抛 std::bad_cast
} catch (const std::bad_cast&) {// 处理失败
}
📝 四、const_cast
const_cast
仅用于添加/移除顶层 const/volatile 限定,不改变对象的实际类型。
1.const_cast 正确用法
- 恢复非常量对象的非常量性(原对象本身不是 const)
void takes_int_ptr(int* p);
int x = 42;
const int* cp = &x; // 顶层 const
takes_int_ptr(const_cast<int*>(cp)); // OK:对象本身非 const
2.const_cast 错误用法
- 去 const 后修改“本来就是 const”的对象(未定义行为)
const int y = 0;
int* py = const_cast<int*>(&y);
*py = 1; // 错:修改真正的 const 对象 => UB
📝 五、reinterpret_cast
reinterpret_cast
进行位级重解释,几乎不做语义保证。谨慎,仅限底层场景。
1.reinterpret_cast 正确用法
- 只做字节级观察(读取对象表示),不跨类型语义访问
int v = 0x12345678;
unsigned char* bytes = reinterpret_cast<unsigned char*>(&v);
// 仅查看/拷贝字节,不把它当作其他类型解引用访问
2.reinterpret_cast 错误用法
- 将不相关类型指针互转后解引用(违反别名/对齐,UB)
double* dp = nullptr;
int* ip = reinterpret_cast<int*>(dp);
*ip = 1; // 错:未定义行为
✅ 速记
- 能隐式就隐式;需显式先 static;多态下行用 dynamic;改限定符找 const;位级重解释才用 reinterpret(慎用)。
🧭 总结与选型建议
转换 | 主要用途 | 运行期检查 | 常见正确用法 | 典型错误用法/风险 |
---|---|---|---|---|
static_cast | 数值/枚举互转;继承上行;已知安全的受控下行 | 否 | 避免整除、选择重载、枚举↔整型、派生→基类 | 不相关指针互转;指针↔整型;不确定类型的下行(UB) |
dynamic_cast | 多态下行/交叉转换 | 是(需虚函数) | 指针判空后使用;引用用 try-catch 捕获失败 | 用在非多态基类;无检查的错误假设 |
const_cast | 添加/移除顶层 const/volatile | 否 | 将“非常量对象的 const 视图”复原 | 去 const 后修改“本为 const”的对象(UB) |
reinterpret_cast | 位级重解释/底层操作 | 否 | 仅做字节观察或与平台相关的低层用途 | 跨不相关类型解引用(别名/对齐导致 UB) |
简要建议
- 能否不转换;能隐式就隐式。
- 需要显式转换,优先 static_cast;多态下行用 dynamic_cast。
- 仅改限定符用 const_cast;位级重解释最后再考虑,且尽量局部化、加注释与断言。