C++ 强制类型转换
C 风格强制转换的坑,以及 C++ 四种具名转换 static_cast、dynamic_cast、const_cast、reinterpret_cast 的适用场景与区别。
在C语言中,类型转换主要有两种:隐式类型转换和显式类型转换。
- 隐式类型转换(Implicit Type Conversion)
在表达式中自动进行的类型转换,不需要使用特定的语法或运算法,比如常见的整数和浮点数之间的转换。
char c ='A';int i =c; //隐式类型转换
- 显示类型转换(Explicit Type Conversion)
在表达式中使用特定的语法或运算符进行的类型转换,也称为强制类型转换。
float f =3.15;int i =(int)f;
在C语言中。类型转换存在一些潜在的问题和不足之处,其中包括:
- 信息丢失:当一个较大的数据类型转换为一个较小的数据类型时,可能会发生信息丢失。例如,将一个浮点数转换为整数时,小数部分会被截断,导致精度丢失。
- 未定义行为:某些情况下,进行不适当的类型转换可能导致未定义的行为,这使得代码的行为不可预测。例如,在指针类型之间进行不正确的转换导致内存访问错误。
#include <stdio.h>int main() {int arr[3] = {1, 2, 3};int *ptr = arr; // ptr指向数组arr的第一个元素printf("%d\n", ptr[0]);// 错误的指针类型转换float *floatPtr = (float *)ptr; // 将int指针转换为float指针// 尝试使用错误类型的指针访问数组元素printf("%f\n", floatPtr[0]); // 未定义行为,可能导致内存访问错误return 0;}
- 数据损坏:类型转换可能会导致数据损坏或失真。例如,将一个整数转换为一个较小的整数类型时,如果超出了目标类型的表示范围,结果可能会不正确。
- 可读性和可维护性:使用类型转换可能会降低代码的可读性和可维护性。在代码中使用大量的类型转换可能会使代码难以理解,并且增加了代码维护的复杂性。
- 难以排查错误:当类型转换被滥用或错误使用时,可能会导致代码中隐藏的错误。这些错误可能很难排查和修复,因为它们可能会在运行时产生不可预测的结果。
在C++中,常见的类型转换方法包括隐式类型转换和显式类型转换,而显示类型转换又可以进一步细分为四种:static_cast、dynamic_cast、const_cast和reinterpret_cast。
-
静态转换(static_cast)
- 用于基本内置数据类型之间的转换
float data = static_cast<float>(1)- 用于指针之间的类型转换
#include <iostream>using namespace std;int main(){int type_int =10;//float* float_ptr1= &type_int; //隐式转换无效 error: cannot convert ‘int*’ to ‘float*’ in initialization//float* float_ptr2= static_cast<float*>(&type_int); //error: invalid static_cast from type ‘int*’ to type ‘float*’float* float_ptr3= (float*)&type_int;cout <<"float_ptr3:"<<*float_ptr3<<endl; //编译器运行通过,但是输出不对float_ptr3:1.4013e-44void* void_ptr=&type_int; //任何指针都可以隐士转换为void*//float* float_ptr4 = void_ptr; //error: invalid conversion from ‘void*’ to ‘float*float* float_ptr5 = static_cast<float*>(void_ptr);cout <<"float_ptr5:"<<*float_ptr5<<endl; //编译器运行通过,但是输出不对float_ptr5:1.4013e-44float * float_ptr6 = (float *) void_ptr;cout <<"float_ptr6:"<<*float_ptr6<<endl; //编译器运行通过,但是输出不对float_ptr6:1.4013e-44return 0;}- 用于类之间的类型转换
#include <iostream>using namespace std;class A{public:int a{1};};class B{public:int b{2};};class C: public A, public B{public:int c{3};};int main(){C c;A* a_ptr =static_cast<A*>(&c);//上行指针转换正常cout<<"A a: "<< a_ptr->a<<endl;B* b_ptr =static_cast<B*>(&c);//上行指针转换正常cout<<"B b: "<< b_ptr->b<<endl;A& a_ref =static_cast<A&>(c);//上行引用转换正常cout<<"A a: "<< a_ref.a<<endl;B& b_ref =static_cast<B&>(c); //上行引用转换正常cout<<"B b: "<< b_ref.b<<endl;A a;B b;C* c_ptr1 =static_cast<C*>(&a);C* c_ptr2 =static_cast<C*>(&b);cout<<"c_ptr1 c: "<<c_ptr1->c<<endl; //2 下行转换指针无效,编译通过,结果不对cout<<"c_ptr2 c: "<<c_ptr2->c<<endl; //1 下行转换指针无效,编译通过,结果不对C& c_ref1 =static_cast<C&>(a);C& c_ref2 =static_cast<C&>(b);cout<<"c_ref1 c: "<<c_ref1.c<<endl; //2 下行转换引用无效,编译通过,结果不对cout<<"c_ref2 c: "<<c_ref2.c<<endl; //1 下行转换引用无效,编译通过,结果不对return 0;}
- 用于具多态的类实例指针或引用之间的转换 -
动态转换(dynamic_cast)
- 用于多态类型的安全转换,通常在基类和派生类之间转换时使用。
- 运行时执行类型检查,只能用于指针或引用类型,并且要求目标类型必须有虚函数。
- 如果转换不安全,返回空指针(在指针转换中),或抛出std::bad_cast异常(在引用转换中)。
Base* basePtr = new Derived();Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 动态转换 -
常量转换(const_cast)
- 用于添加或删除’const’、’volatile’修饰符。
- 主要用于修改常量对象的常量性或者将常量引用转换为非常量引用,以允许对其进行修改。
const int x = 5;int& y = const_cast<int&>(x); // 取消常量性y = 10; // 合法 -
重解释转换(reinterpret_cast)
- 用于不同类型之间的位模式重新解释。
- 是一种较低级别的转换,可以将任何指针类型转换为其他指针类型,也可以将任何整数类型转换为指针类型,反之亦然
- 非常危险,可能导致未定义的行为,应谨慎使用。
int* ptr = reinterpret_cast<int*>(somePointer);