命名的强制类型转换:
cast-name(expression) : type是转换的目标类型,expression是要转换的值。如果type是引用类型,则结果是左值。
1、static_cast
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast
主要用途:
(1) 常规的使用方法
double slope = static_cast<double>(j)/i; //进行强制类型转换以便执行浮点数除法
(2) class的上下文转换:用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换
注意点:
1、进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
2、进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的
例:
1、上行转换:
class Base{}
class Sub : public Base{}
//上行 Sub -> Base
//编译通过,安全
Sub sub;
Base *base_ptr = static_cast<Base*>(&sub);
2、下行转换:
//编译出错,不安全
Sub *sub_ptr = static_cast<Sub*>(&base);
(3)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性需要开发者来维护。
(4)static_cast不能转换掉原有类型的const、volatile、或者 __unaligned属性。(前两种可以使用const_cast 来去除)
2、dynamic_cast
作为C++实现运行时类型识别(RTTI)功能的运算符,dynamic_cast的强制类型转换涉及到面向对象的多态性和程序运行时的状态,也与编译器的属性设置有关.所以不能完全使用C语言的强制转换替代,它也是最常有用的,最不可缺少的一种强制转换.
主要形式如下:
(1)dynamic_cast<type*>(e)
(2)dynamic_cast<type&>(e)
(3)dynamic_cast<type&&>(e)
两种应用场景:
(1)指针类型的dynamic_cast:
Base类至少有一个虚函数,Derived是Base的共有派生类,如果有一个指向Base的指针bp,则我们可以在运行时将它
转换成指向Derived的指针,具体代码:
if(Derived *dp = dynamic_cast<Derived*>(bp))
{
//使用dp指向的Derived对象
}else{ //bp指向一个Base对象
//使用bp指向的Base对象
}
(2)应用类型的dynamic_cast:
void f(const Base& b)
{
try{
const Derived &d = dynamic_cast<const Derived&>(b);
//使用b引用的Derived对象
}catch(bad_cast){
//处理类型转换失败的情况
}
}
举例:
#include<iostream>
using namespace std;
class Base{
public:
Base() {}
~Base() {}
void print() {
std::cout << "I'm Base" << endl;
}
virtual void i_am_virtual_foo() {}
};
class Sub: public Base{
public:
Sub() {}
~Sub() {}
void print() {
std::cout << "I'm Sub" << endl;
}
virtual void i_am_virtual_foo() {}
};
int main() {
cout << "Sub->Base" << endl;
Sub * sub = new Sub();
sub->print();
Base* sub2base = dynamic_cast<Base*>(sub);
if (sub2base != nullptr) {
sub2base->print();
}
cout << "<sub->base> sub2base val is: " << sub2base << endl;
cout << endl << "Base->Sub" << endl;
Base *base = new Base();
base->print();
Sub *base2sub = dynamic_cast<Sub*>(base);
if (base2sub != nullptr) {
base2sub->print();
}
cout <<"<base->sub> base2sub val is: "<< base2sub << endl;
delete sub;
delete base;
return 0;
}
/* Linux gcc 输出为下
Sub->Base
I'm Sub
I'm Base
<sub->base> sub2base val is: 00B9E080 // 注:这个地址是系统分配的,每次不一定一样
Base->Sub
I'm Base
<base->sub> base2sub val is: 00000000 // VS2017的C++编译器,对此类错误的转换赋值为nullptr
*/
从实验结果可以看出:
1、对于从子类到基类的指针转换 ,dynamic_cast 成功转换,没有什么运行异常,且达到预期结果
2、而从基类到子类的转换 , dynamic_cast 在转换时也没有报错,但是输出给 base2sub 是一个 nullptr,说明dynami_cast 在程序运行时对类型转换对“运行期类型信息”(Runtime type information,RTTI)进行了检查。
这个检查主要来自虚函数(virtual function) 在C++的面对对象思想中,虚函数起到了很关键的作用,当一个类中拥有至少一个虚函数,那么编译器就会构建出一个虚函数表(virtual method table)来指示这些函数的地址,假如继承该类的子类定义并实现了一个同名并具有同样函数签名(function siguature)的方法重写了基类中的方法,那么虚函数表会将该函数指向新的地址。此时多态性就体现出来了:当我们将基类的指针或引用指向子类的对象的时候,调用方法时,就会顺着虚函数表找到对应子类的方法而非基类的方法。
3、const_cast
只能修改运算对象的底层const,并且只有const_cast能够修改表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型。
const char* pc;
char *p = const_cast<char*>(pc); //正确,但是通过p写值是未定义的行为
const char* cp;
char *q = static_cast<char*>(cp);//错误,static_cast不能转换掉const性质
static_cast<string>(cp); //正确:字符串字面量转换成string类型
const_cast<string>(cp); //错误:const_cast只改变常量属性
如果对象本身不是一个常量,使用强制类型转换获得写权限是合法行为。
如果对象本身是一个常量,使用const_cast执行写操作就会产生未定义的后果。
主要用途:
(1)常量指针被转化成非常量的指针,并且仍然指向原来的对象;
(2)常量引用被转换成非常量的引用,并且仍然指向原来的对象;
(3)const_cast一般用于修改指针。如const char *p形式。
4、reinterpret_cast
reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。用在任意的指针之间的转换,引用之间的转换,指针和足够大的int型之间的转换,整数到指针的转换。
#include<iostream>
#include<cstdint>
using namespace std;
int main() {
int *ptr = new int(233);
uint32_t ptr_addr = reinterpret_cast<uint32_t>(ptr);
cout << "ptr 的地址: " << hex << ptr << endl
<< "ptr_addr 的值(hex): " << hex << ptr_addr << endl;
delete ptr;
return 0;
}
/*
ptr 的地址: 0061E6D8
ptr_addr 的值(hex): 0061e6d8
*/