一. 隐式类型转换
含义:隐式类型转换:系统自动进行,不需要开发人员介入。
int m = 3 + 45.6; //48
因为返回的int
型,所以系统自动去除掉小数点后面的值,结果为48,这种属于隐式转换类型。
二. 显式类型转换(强制类型转换)
1、C
语言的强制类型转换风格:
//int k = 5 % 3.2; //语法错误:%非法,右操作数包含“double”类型 int k = 5 % int(3.2); //将3.2强制转换成int型3,C语言风格的强制类型转换,结果:余2
上面这种C
语言的强制类型转换风格,没有类型检查,往整型上硬转,转的对不对需要程序员提供保障,例如强制将字符串类型转换成int
类型:(int)"asa"
就不行了。
2、针对上面的情况,C++
提供了4种更为安全的强制类型转换:
static_cast
显式转换类型;dynamic_cast
显式转换类型;const_cast
显式转换类型;reinterpret_cast
显式转换类型;
C++
强制类型转换通用书写形式:强制类型转换名 <type> ( express );
- 强制类型转换名:
static_cast
、dynamic_cast
、const_cast
、reinterpret_cast
4种类型 - type:转换到的目标类型
- express:待转换的类型(值或变量),即
express
转换成type
类型
每一种强制类型转换都有其特定的目的,这样可以提供更丰富的含义和功能、更好的类型检查机制,方便代码的编写和维护。
static_cast显示转换
含义:static_cast
:静态转换,理解成“正常转换”(编译时进行类型检查)。
适用场合:
1、相关类型转换,比如整型int
和实型qeal
之间的转换。
double f = 100.34; int ic1 = f; //方式一:隐式转换直接转 int ic1 = int(f); //方式二:C风格的强制类型转换 int ic2 = static_cast<int>(f); //方式三:C++风格的强制类型转换
2、继承关系中子类转换成父类类型(向上转换:隐式转换
),可以直接转换,也可以使用static_cast
转。
class A {}; class B : public A {}; A a; B b; a = b; //方式一:隐式转换直接转 a = static_cast<A>(b); //方式二:static_cast转换
3、void *
与其他类型指针之间的转换。void *
:无类型指针(万能指针):可以指向任何指针类型
int i = 10; int *p = &i; void *q = p; //方式一:隐式转换,系统内部自己转 void *q = static_cast<void *>(p); //方式二:static_cast转换
不适用场合:
1、指针类型之间的转换,比如int *
转double *
,double *
转float *
等。
double d = 100.34; double *pd = &d; int *pi = static_cast<int *>(pd); //达咩,类型转换无效
小结:static_cast
含义跟C
语言中的强制类型转换差不多:
C
风格的强制类型转换和编译器自己能够进行的隐式类型转换都可以用static_cast
显示完成转换(一般隐式转换让系统内部自己转就好了,不需要static_cast
转换)。- 与
C
风格的强制类型转换一样,使用static_cast
也要保证转换的安全性和正确性,比如int i = (int)“asa”
这种情形不应该出现。
dynamic_cast显示转换
含义:dynamic_cast
:能够将基类
的指针
或引用
安全的转换为派生类的
指针或
引用(运行时进行类型检查)。
ps:只要dynamic_cast
运算符能转换成功,就说明这个指针实际上是要转换到的那个类型,否则不能转换成功。这也间接的帮助我们完成了运行时的类型识别和安全检查(弥补C
语言风格的强制转换式的不足)。
适用场合:
1、有继承关系父类指针转换成子类指针(向下显示转换)
struct Father{ /*父类Father*/ }; struct Son: Father{ /*基类Father的子类B*/ }; Father *father = new Son; Son *son = (Son *)(father); //C风格的强制类型转换:硬把父类指针Father *转换成子类指针Son *,可以 //son->子类成员 //可以,能够正常调用Son类的成员函数
这种转换需要我们明确父类指针father
是指向子类Son
对象的(Father *father = new Son
)才是安全的。否则明明father是指向Son对象的,却被强制转换成其他类的指针,再调用就不安全了。此外,如果在他人的代码中看到一个指针father
,想要确认这个指针到底是指向本身类Father
对象的、子类Son
对象还是其他类的对象就太好区分,所以应该使用dynamic_cast
来完成父类指针到子类指针的强制转换:
struct Father{ /*父类Father*/ }; struct Son: Father{ /*基类Father的子类B*/ }; Father *father = new Son; Son *son = dynamic_cast<Son *>(father); if (son != nullptr) //对于引用,如果用dynamic_cast转换失败,系统会抛出std::bad_cast异常 { cout << "father实际是一个Son类型" << endl; //在这里操作Son里面的成员函数、成员变量都能够操作并且安全的操作 }
注意:使用dynamic_cast
显示转换父类指针时要保证父类中一定要有虚函数virtual,否则会报错:运行dynamic_cast
操作符必须包含多态类类型。
2、有继承关系父类引用转换成子类引用(用的较少,了解)
struct Father{ /*父类Father*/ }; struct Son: Father{ /*基类Father的子类B*/ }; Father *father1 = new Son; Father &father2 = *father1; try { Son &son = dynamic_cast<Son &>(father2); //转换成功 } catch (std::bad_cast) { cout << "father2实际不是一个Son类型" << endl; }
const_cast显示转换
含义:const_cast
:去除指针或引用的const
属性。该转换能够将const
性质转换掉或去除掉(编译时进行类型检查)。
错误示范:
const int ai = 90; int ai2 = const_cast<int>(ai);
结果:
正确示范:
const int ai = 90; const int *ai2 = &ai; int *ai3 = const_cast<int *>(ai2); //截止到这里都没问题 *ai3 = 120; //注意:这种写值行为是一种未定义行为(实际结果不可控,因为*ai3转换之前是常量const int ai,所以不要往里写值,正常调用指针对象没问题)
reinterpret_cast显示转换
reinterpret_cast
: 处理无关类型之间的转换,也就是两个转换类型之间没有什么关系,就等于可以乱转、自由转,非常随意(编译时进行类型检查)
ps: reinterpret
:重新解释,将操作数内容解释为另一种不同的类型(能把操作数的类型都转了)。
适用场合:
1、将一个整型(地址)转换成指针,一种类型指针转换成另一种类型指针,按照转换后的类型重新解释内存中的内容。
2、将一个指针类型转换成一个整型。
int i = 10; int *p= &i; int *pi = reinterpret_cast<int *>(&i); //这个没啥说的 char *pc = reinterpret_cast<char *>(p); //int *也可以转换成char *,语法对,但是没什么屌用
ps:reinterpret_cast
被认为是危险的类型转换,虽然其可以随便转换,而且编译器也不报错,但是需要合乎规则的用,不然就没什么意义了。这里了解一下,防止看别人代码不知道什么意思。
三. 总结
- 所有强制类型转换,不建议使用,强制类型转换能够抑制编译器报错。
- 学习目的:认识这些类型转换符,方便大家阅读别人代码。
- 资料说:
reinterpret_cast
危险,我们需要合乎规则的用,不要乱用;使用const_cast
意味着设计缺陷。 - 如果实在需要使用强制类型转换,不要再使用
C
语言风格的类型转换了,而是用上面C++
风格的类型转换,一般static_cast
和reinterpret_cast
能够很好的取代C
语言风格的强制类型转换。
下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。 |