核心前提
C++ 引入命名类型转换的核心目的是:让类型转换的意图更明确,编译器能做更严格的类型检查,减少隐式转换带来的安全隐患。这四种转换各有明确的适用边界,不能混用。
一、static_cast:最常用的 "合法" 转换
1. 核心定义
static_cast<目标类型>(源对象) 是编译期完成的类型转换,编译器会检查转换的 "逻辑合理性",但不做运行时检查。
2. 适用场景(按优先级排序)
(1)基础数据类型的显式转换(替代 C 风格转换)
- 适用于:数值类型(int/double/char)、布尔类型之间的转换。
- 注意:高精度→低精度会截断(如 double→int),编译器会警告但允许。
cpp
运行
#include <iostream>
using namespace std;
int main() {
// 场景1:浮点→整数(截断小数部分)
double pi = 3.1415926;
int pi_int = static_cast<int>(pi); // 结果:3
cout << "pi转整数: " << pi_int << endl;
// 场景2:整数→浮点(安全,无精度丢失)
int num = 100;
double num_double = static_cast<double>(num); // 结果:100.0
cout << "整数转浮点: " << num_double << endl;
// 场景3:char→int(ASCII值转换)
char c = 'A';
int c_int = static_cast<int>(c); // 结果:65
cout << "char转int: " << c_int << endl;
return 0;
}
(2)类层次的 "向上转型"(子类→父类)
- 适用于:公有继承体系中,子类对象 / 指针 / 引用转为父类类型(符合面向对象的 "里氏替换原则")。
- 特点:完全安全,无任何风险。
cpp
运行
#include <iostream>
using namespace std;
class Parent { // 父类
public:
void show() { cout << "Parent class" << endl; }
};
class Child : public Parent { // 子类(公有继承)
public:
void show_child() { cout << "Child class" << endl; }
};
int main() {
Child c;
// 子类对象→父类对象(切片:仅保留父类部分)
Parent p = static_cast<Parent>(c);
p.show(); // 输出:Parent class
// 子类指针→父类指针(更常用)
Parent* p_ptr = static_cast<Parent*>(&c);
p_ptr->show(); // 输出:Parent class
return 0;
}
(3)void* 与其他类型指针的转换
- 适用于:
void*↔ 其他类型指针(C++ 中唯一合法的void*显式转换方式)。 - 注意:
void*无法直接解引用,转换后才能使用。
cpp
运行
#include <iostream>
using namespace std;
int main() {
int num = 100;
// int* → void*(隐式转换也可,但static_cast更明确)
void* void_ptr = static_cast<void*>(&num);
// void* → int*(必须显式转换)
int* int_ptr = static_cast<int*>(void_ptr);
cout << "void*转回int*: " << *int_ptr << endl; // 输出:100
return 0;
}
(4)反向转换(需谨慎)
- 允许:父类→子类(向下转型)、非 const→const(与 const_cast 相反),但编译器不做任何检查,可能导致未定义行为。
- 风险:如果父类指针实际指向的不是子类对象,转换后调用子类独有成员会崩溃。
3. 核心特点
- 编译期检查,无运行时开销;
- 仅检查 "语法合法性",不检查 "逻辑合理性";
- 不能移除
const/volatile属性,不能转换不相关的指针类型(如int*→char*)。
二、const_cast:仅修改常量属性的转换
1. 核心定义
const_cast<目标类型>(源对象) 是唯一能修改变量const/volatile属性的转换方式,仅作用于指针 / 引用 / 成员函数指针,不改变类型本身。
2. 适用场景
(1)移除指针 / 引用的 const 属性(最常用)
- 核心用途:调用不支持 const 参数的函数,但需确保原变量实际不是 const(否则修改会导致未定义行为)。
cpp
运行
#include <iostream>
using namespace std;
// 函数要求非const参数
void modify_value(int* ptr) {
*ptr = 200;
}
int main() {
// 场景1:const变量(不建议修改,可能未定义)
const int const_num = 100;
int* non_const_ptr = const_cast<int*>(&const_num);
modify_value(non_const_ptr);
// 输出可能是100(编译器优化)或200,行为未定义
cout << "修改const变量: " << const_num << endl;
// 场景2:非const变量被const指针指向(安全)
int normal_num = 100;
const int* const_ptr = &normal_num;
int* normal_ptr = const_cast<int*>(const_ptr);
modify_value(normal_ptr);
cout << "修改非const变量: " << normal_num << endl; // 输出:200
return 0;
}
(2)添加 const 属性(反向操作)
- 用途:将非 const 指针 / 引用转为 const,符合函数参数要求(更安全)。
cpp
运行
#include <iostream>
using namespace std;
void print_const(const int* ptr) {
cout << "值: " << *ptr << endl;
}
int main() {
int num = 100;
int* ptr = #
// 非const→const(安全,推荐)
const int* const_ptr = const_cast<const int*>(ptr);
print_const(const_ptr); // 输出:100
return 0;
}
3. 核心规则与风险
- ❌ 不能转换基础类型(如
const_cast<int>(100)编译报错); - ❌ 不能修改真正的 const 变量(如
const int a=10),否则行为未定义; - ✅ 仅修改 "指针 / 引用的 const 属性",不改变原变量的本质;
- ✅ 是唯一能处理 const/volatile 的转换方式。
三、dynamic_cast:运行时类型检查的安全转换
1. 核心定义
dynamic_cast<目标类型>(源对象) 是唯一支持运行时类型检查 的转换方式,仅适用于多态类(包含虚函数的类)的指针 / 引用转换。
2. 核心原理
- 编译期:编译器检查转换的语法合法性;
- 运行时:通过对象的虚函数表(vtable) 获取实际类型信息,判断转换是否合法。
3. 适用场景:类层次的向下转型
(1)父类指针→子类指针(最核心场景)
- 转换成功:返回子类指针;
- 转换失败:返回
nullptr(指针)/ 抛出bad_cast异常(引用)。
cpp
运行
#include <iostream>
#include <typeinfo> // 需包含此头文件
using namespace std;
// 多态父类(必须有虚函数)
class Parent {
public:
virtual void show() { cout << "Parent" << endl; }
virtual ~Parent() {} // 虚析构,避免内存泄漏
};
class ChildA : public Parent {
public:
void show() override { cout << "ChildA" << endl; }
void childA_func() { cout << "ChildA独有函数" << endl; }
};
class ChildB : public Parent {
public:
void show() override { cout << "ChildB" << endl; }
};
int main() {
// 场景1:父类指针实际指向子类A → 转换为ChildA*(成功)
Parent* p1 = new ChildA();
ChildA* ca = dynamic_cast<ChildA*>(p1);
if (ca) { // 判空是关键!
ca->childA_func(); // 输出:ChildA独有函数
}
// 场景2:父类指针实际指向子类A → 转换为ChildB*(失败)
ChildB* cb = dynamic_cast<ChildB*>(p1);
if (!cb) {
cout << "转换ChildB失败" << endl; // 输出此句
}
// 场景3:父类指针指向父类对象 → 转换为子类(失败)
Parent* p2 = new Parent();
ChildA* ca2 = dynamic_cast<ChildA*>(p2);
if (!ca2) {
cout << "转换子类失败" << endl; // 输出此句
}
delete p1;
delete p2;
return 0;
}
(2)父类引用→子类引用
- 引用不能为
nullptr,转换失败会抛出std::bad_cast异常,需用 try-catch 捕获。
cpp
运行
#include <iostream>
#include <typeinfo>
using namespace std;
class Parent { virtual void show() {} };
class Child : public Parent {};
int main() {
Parent p;
Parent& ref_p = p;
try {
Child& ref_c = dynamic_cast<Child&>(ref_p);
} catch (const bad_cast& e) {
cout << "转换失败: " << e.what() << endl; // 输出异常信息
}
return 0;
}
4. 核心规则
- ✅ 仅适用于多态类(有虚函数),否则编译报错;
- ✅ 向下转型(父→子)时,运行时检查实际类型,是最安全的向下转型方式;
- ✅ 向上转型(子→父)也支持,但效果等同于
static_cast(无必要); - ❌ 不能转换基础类型,不能转换非多态类;
- ⚠️ 有运行时开销(需查询类型信息),不宜在高频循环中使用。
四、reinterpret_cast:最危险的 "内存重解释" 转换
1. 核心定义
reinterpret_cast<目标类型>(源对象) 是最底层的转换方式,直接重新解释内存的二进制布局,编译器几乎不做任何类型检查,仅保证 "源类型和目标类型的大小一致"。
2. 适用场景(仅限特殊底层场景)
(1)不相关指针类型的转换
- 用途:如
int*→char*、函数指针→void*等(仅用于硬件编程、调试等特殊场景)。
cpp
运行
#include <iostream>
using namespace std;
int main() {
// 场景1:int* → char*(按字节读取内存)
int num = 0x41424344; // 十六进制:对应ASCII字符 A(41), B(42), C(43), D(44)
char* char_ptr = reinterpret_cast<char*>(&num);
// 输出取决于系统大小端:小端(x86)输出 DCBA,大端输出 ABCD
cout << "内存字节内容: " << char_ptr << endl;
// 场景2:函数指针→void*(C++标准未定义,但多数编译器支持)
void func() { cout << "测试函数" << endl; }
void* func_ptr = reinterpret_cast<void*>(&func);
// 转回函数指针并调用
void (*f)() = reinterpret_cast<void(*)()>(func_ptr);
f(); // 输出:测试函数
return 0;
}
(2)指针与整数的转换
- 用途:将指针地址转为整数(如 uintptr_t),便于打印或存储地址。
cpp
运行
#include <iostream>
#include <cstdint> // 包含uintptr_t
using namespace std;
int main() {
int num = 100;
int* ptr = #
// 指针→整数(uintptr_t是专门存储指针的整数类型)
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
cout << "指针地址: " << hex << addr << endl; // 输出指针的十六进制地址
// 整数→指针
int* ptr2 = reinterpret_cast<int*>(addr);
cout << "转回指针取值: " << *ptr2 << endl; // 输出:100
return 0;
}
3. 核心风险与规则
- ❌ 极度危险:转换后的类型若不合理使用(如
int*转float*后解引用),会导致程序崩溃或未定义行为; - ❌ 不保证跨平台兼容性:内存布局依赖编译器和 CPU 架构;
- ✅ 仅用于底层编程(如驱动、调试、硬件交互),普通业务代码严禁使用;
- ✅ 不能移除
const属性(需配合const_cast)。
总结:四种命名转换的核心对比
| 转换类型 | 检查时机 | 核心用途 | 安全性 | 适用范围 |
|---|---|---|---|---|
static_cast |
编译期 | 基础类型转换、向上转型、void * 转换 | 中等 | 基础类型、类指针 / 引用 |
const_cast |
编译期 | 修改指针 / 引用的 const/volatile 属性 | 低 | 指针 / 引用、成员函数指针 |
dynamic_cast |
运行期 | 多态类的向下转型(安全检查) | 高 | 多态类的指针 / 引用 |
reinterpret_cast |
编译期 | 内存二进制重解释(不相关指针 / 整数转换) | 极低 | 指针、整数、函数指针 |
关键使用原则
- 优先使用
static_cast(日常最常用,意图明确); - 处理 const 属性仅用
const_cast(且仅修改非真正 const 的变量); - 多态类向下转型必须用
dynamic_cast(并判空 / 捕获异常); reinterpret_cast仅用于底层特殊场景,普通代码禁止使用;- 任何转换都需明确 "为什么转",避免无意义的类型转换。