C++ 类型转换深度解析:static_cast、dynamic_cast、const_cast、reinterpret_cast
引言在 C 语言中类型转换的语法非常简单——只需在变量前加上目标类型即可。但这种转换缺乏安全性检查容易导致未定义行为。C 引入了四种命名的强制类型转换运算符它们提供了更细粒度的转换控制和更高的可读性。四种类型转换分别是static_cast静态转换编译时确定dynamic_cast动态转换运行时检查依赖 RTTIconst_cast常量转换添加或移除 const 属性reinterpret_cast重解释转换最不安全的底层转换今天我将从使用场景、底层原理和安全性角度全面讲解这四种类型转换。第一部分静态转换static_cast一、基本概念static_cast是最常用的类型转换用于编译时可以确定的类型转换。它在编译期进行类型检查比 C 风格转换更安全。static_cast目标类型(源表达式)二、支持的类型转换1. 基本数据类型之间的转换#include iostream using namespace std; int main() { // 整数与浮点数转换 double pi 3.14159; int pi_int static_castint(pi); // 3小数部分截断 cout pi_int pi_int endl; // 整数与字符转换 char ch A; int ascii static_castint(ch); // 65 cout ASCII of A ascii endl; // 大类型转小类型可能溢出 long long big 99999111001010LL; int small static_castint(big); // 截断高位 cout long long to int: small endl; return 0; }2. void* 与任意类型指针的互转int main() { int n 100; // int* → void* void* vp static_castvoid*(n); // void* → char* char* cp static_castchar*(vp); // 通过 char* 逐字节访问 for (int i 0; i sizeof(int); i) { printf(%02X , (unsigned char)cp[i]); } cout endl; return 0; }3. 基类与派生类之间的转换class F { public: void show() { cout F show() endl; } }; class S : public F { public: void show() { cout S show() endl; } }; int main() { F f; S s; // 子类对象转换为父类对象切片 F f2 static_castF(s); // 调用拷贝构造派生类部分被切掉 f2.show(); // F show() // 父类指针转子类指针危险但编译允许 F* fp f; S* sp2 static_castS*(fp); sp2-show(); // S show()但对象实际是F访问派生类成员会出问题 // 子类指针转父类指针安全 S* sp s; F* fp2 static_castF*(sp); fp2-show(); // F show() return 0; }重要规则非指针/引用时子类对象可以转换为父类对象切片父类对象不能转换为子类对象指针/引用时子类指针可以转换为父类指针父类指针转子类指针需要谨慎4. 左值转右值引用std::move 的本质#include utility // for std::move int main() { int n 100; // static_cast 实现左值转右值引用 int rref1 static_castint(n); // std::move 内部就是 static_cast int rref2 std::move(n); cout rref1 rref1 endl; cout rref2 rref2 endl; return 0; }第二部分动态转换dynamic_cast一、基本概念dynamic_cast是 C 中唯一的运行时类型转换它依赖于RTTIRuntime Type Information运行时类型识别机制。转换失败时指针版本返回nullptr引用版本抛出std::bad_cast异常。dynamic_cast目标类型(源表达式)二、使用前提关键条件基类必须至少有一个虚函数因为 RTTI 信息存储在虚函数表中。class A { public: virtual void hi() { cout A hi() endl; } // 必须有虚函数 }; class B : public A { public: void hi() override { cout B hi() endl; } };三、向上转型子类 → 父类向上转型总是安全的dynamic_cast和static_cast效果相同。int main() { B b; // 子类对象转父类引用 A a_ref dynamic_castA(b); a_ref.hi(); // B hi()多态 // 子类指针转父类指针 A* a_ptr dynamic_castA*(b); a_ptr-hi(); // B hi() return 0; }四、向下转型父类 → 子类向下转型是dynamic_cast的核心应用它会在运行时检查类型是否匹配。int main() { A a; B b; // 情况1父类指针指向子类对象 → 转换成功 A* a_ptr_to_b b; B* b_ptr dynamic_castB*(a_ptr_to_b); if (b_ptr ! nullptr) { cout 转换成功; b_ptr-hi(); // B hi() } // 情况2父类指针指向父类对象 → 转换失败 A* a_ptr_to_a a; B* b_ptr2 dynamic_castB*(a_ptr_to_a); if (b_ptr2 nullptr) { cout 转换失败返回 nullptr endl; } // 引用的版本转换失败会抛出异常 try { B b_ref dynamic_castB(a); b_ref.hi(); } catch (const bad_cast e) { cout 引用转换失败 e.what() endl; } return 0; }五、dynamic_cast 的原理第三部分常量转换const_cast一、基本概念const_cast用于添加或移除变量的const或volatile属性。它是唯一能处理常量的转换运算符。const_cast目标类型(源表达式)二、移除 const 属性int main() { const int n 10; // 移除 const获得可修改的指针 int* np const_castint*(n); *np 100; cout np np , *np *np endl; cout n n , n n endl; // 注意虽然地址相同但值可能不同常量折叠优化 // 编译器可能将 n 替换为字面量 10导致输出不一致 return 0; }三、添加 const 属性int main() { int n 10; int* np n; // 添加 const 属性 const int* cnp const_castconst int*(np); // *cnp 20; // 错误不能通过 const 指针修改 // 引用版本 int nr n; const int cnr const_castconst int(nr); return 0; }四、重要警告int main() { // 危险修改真正的 const 变量是未定义行为 const int const_value 100; int* p const_castint*(const_value); *p 200; // 未定义行为可能导致程序崩溃或值不变 // 安全修改的是一开始就不是 const 的变量 int mutable_value 100; const int ref mutable_value; int* q const_castint*(ref); *q 200; // 安全因为 mutable_value 本身不是 const cout mutable_value mutable_value endl; return 0; }第四部分重解释转换reinterpret_cast一、基本概念reinterpret_cast是最不安全的类型转换它直接重新解释内存中的二进制位不进行任何类型检查。主要用于底层编程、硬件访问、序列化等场景。reinterpret_cast目标类型(源表达式)二、指针类型之间的重解释#include iostream using namespace std; class A { public: int x 1; int y 2; }; class B { public: int arr[2]{5, 9}; }; int main() { A a; B b; // 将 A 对象重解释为 B 对象 B bf reinterpret_castB(a); cout bf.arr[0] , bf.arr[1] endl; // 1,2 // 将 a.x 解释为 arr[0]a.y 解释为 arr[1] return 0; }三、任意类型指针互转int main() { string s 123; // 将 string 对象地址重解释为 int* int* n reinterpret_castint*(s); // cout *n endl; // 危险将 string 的内存解释为 int // 将 string 对象地址重解释为 char*逐字节查看 char* p reinterpret_castchar*(s); for (int i 0; i sizeof(string); i) { cout hex (int)(unsigned char)p[i] ; } cout endl; return 0; }四、整数与指针互转int main() { int addr 0x12345678; // 整数转指针危险通常用于嵌入式或驱动开发 int* p reinterpret_castint*(addr); // *p 100; // 如果地址无效程序崩溃 // 指针转整数 int val reinterpret_castlong long(p); cout 指针值作为整数: hex val endl; return 0; }第五部分四种转换对比总结一、功能对比表转换类型使用场景安全性运行时开销是否依赖 RTTIstatic_cast基本类型转换、向上转型、void*转换中等无否dynamic_cast多态类型向下转型高运行时检查有是const_cast添加/移除 const 属性低需谨慎无否reinterpret_cast底层二进制重解释极低无否二、使用建议// 1. 优先使用 static_cast 代替 C 风格转换 int a 10; double b static_castdouble(a); // 推荐 double c (double)a; // 不推荐 // 2. 多态向下转型使用 dynamic_cast Base* base new Derived(); Derived* derived dynamic_castDerived*(base); if (derived) { // 安全使用 derived } // 3. 只在必要时使用 const_cast且确保原值不是 const void readOnly(const int* p) { // 如果确定 p 指向的是非 const 变量 int* writable const_castint*(p); *writable 100; } // 4. 尽量避免使用 reinterpret_cast除非底层编程 // 序列化/反序列化时可用三、类型转换选择流程图第六部分完整示例#include iostream #include typeinfo using namespace std; // 基类必须有虚函数才能使用 dynamic_cast class Animal { public: virtual void speak() const { cout Animal speaks endl; } virtual ~Animal() {} }; class Dog : public Animal { public: void speak() const override { cout Woof! endl; } void wagTail() const { cout Tail wagging endl; } }; class Cat : public Animal { public: void speak() const override { cout Meow! endl; } void purr() const { cout Purring endl; } }; // 演示四种转换 int main() { cout 1. static_cast 演示 endl; int i 10; double d static_castdouble(i); // 基本类型转换 cout int to double: d endl; void* vp static_castvoid*(i); int* ip static_castint*(vp); cout void* to int*: *ip endl; cout \n 2. dynamic_cast 演示 endl; Animal* animals[2] {new Dog(), new Cat()}; for (int i 0; i 2; i) { animals[i]-speak(); // 向下转型 Dog* dog dynamic_castDog*(animals[i]); if (dog) { dog-wagTail(); } Cat* cat dynamic_castCat*(animals[i]); if (cat) { cat-purr(); } } cout \n 3. const_cast 演示 endl; int value 100; const int const_ref value; // 移除 const安全因为原值不是 const int mutable_ref const_castint(const_ref); mutable_ref 200; cout Original value: value endl; const int const_value 300; int* p const_castint*(const_value); // *p 400; // 危险未定义行为 cout \n 4. reinterpret_cast 演示 endl; long long addr 0x12345678; int* ptr reinterpret_castint*(addr); cout Address as pointer: ptr endl; float f 3.14f; int* f_as_int reinterpret_castint*(f); cout Float as int: hex *f_as_int dec endl; // 清理内存 delete animals[0]; delete animals[1]; return 0; }总结一、四种转换核心要点转换关键词适用场景注意事项static_cast编译时静态转换基本类型、向上转型、void*转换向下转型不安全dynamic_cast运行时动态转换多态类型的向下转型必须有虚函数有运行时开销const_cast常量转换添加/移除 const不要修改真正的 const 变量reinterpret_cast重解释转换底层二进制操作极不安全谨慎使用二、原则优先使用 C 风格转换可读性更好意图更明确避免使用 reinterpret_cast除非绝对必要dynamic_cast 需要虚函数这是 RTTI 工作的前提const_cast 要小心修改真正 const 变量是未定义行为C 的四种类型转换提供了比 C 风格转换更精细的控制。理解它们的区别和适用场景能够帮助你写出更安全、更可读的代码。学习建议日常开发优先使用static_cast多态向下转型使用dynamic_cast只在必要时使用const_castreinterpret_cast仅在底层编程中使用