C++不是类型安全的,C++有4种强制类型转换,分别为:
static_cast
dynamic_cast
const_cast
reinterpret_cast
二、static_cast(编译时类型检查)
主要用法:
(1)基本数据类型之间的转换,如把int转换为char,把int转换成enum;
(2)把空指针转换成目标类型的空指针;
(3)把任何类型的表达式类型转换成void类型;
(4)用于类层次结构中父类和子类之间指针和引用的转换(只能在有相互联系的类型中进行相互转换,不一定包含虚函数)。
第(4)点中,存在两种形式的转换,即上行转换(子类到父类)和下行转换(父类到子类)。对于static_cast,上行转换时安全的,而下行转换时不安全的,因为static_cast的转换时粗暴的,它仅根据类型转换语句中提供的信息(尖括号中的类型)来进行转换,这种转换方式对于上行转换,由于子类总是包含父类的所有数据成员和函数成员,因此从子类转换到父类的指针对象可以没有任何顾虑的访问其(指父类)的成员。而对于下行转换,因为static_cast只是在编译时进行类型坚持,没有运行时的类型检查。
三、dynamic_cast(运行时类型检查)
主要用法:
(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查;
(2)不能用于内置的基本数据类型的强制转换;
(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL;
(4)使用dynamic_cast进行转换,父类中一定要有虚函数,否则编译不通过。
(5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
向下转换即指将父类指针转化子类指针,向下转换的成功与否与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。
需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让父类指针或引用指向子类对象的情况,此时转换才有意义。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
四、const_cast
const_cast主要是用来去除const和volatile属性(没有真正去除),当然也可以加上const和volatile属性。
用法:const_cast<type>(expression)
const_cast只可以对指针和引用使用,不可以对对象用。
常量指针被转化成非常量的指针,并且仍然指向原来的对象;
常量引用被转换成非常量的引用,并且仍然指向原来的对象。
通过const_cast运算符,也只能将const type转换为type,将const type&转换为type&,即源类型和目标类型除了const属性不同,其他地方完全相同,转换之后仍是原来的内存,只是变量的属性变了,看待该内存的方式变了。
例子:
常量指针被转化成非常量的指针
volatile const int a = 10; //volatile防止编译器优化,让a每次都从内存读值 cout << a << endl; //此时a的值为10 //a = 20; //编译会出错,由于a被const修饰 int *p = const_cast<int*>(&a); //常量对象转换为非常量对象 *p = 20; cout << a << endl; //此时a的值为20
常量引用被转换成非常量的引用
volatile const int a = 10; //volatile防止编译器优化,让a每次都从内存读值 cout << a << endl; //此时a的值为10 //a = 20; //编译会出错,由于a被const修饰 int &p = const_cast<int&>(a); //常量对象转换为非常量对象 p = 20; cout << a << endl; //此时a的值为20
五、reinterpret_cast
有着和C风格的强制转换同样的能力。它可以转化任何内置的数据类型为其他任何的数据类型,也可以转化任何指针类型为其他的类型。它甚至可以转化内置的数据类型为指针,无须考虑类型安全或者常量的情形。不到万不得已绝对不用。
六、static_cast和dynamic_cast区别
C++中层次类型转换中有两种:上行转换(将子类指针转化父类指针)和下行转换(将父类指针转化子类指针)。
对于上行转换,static_cast和dynamic_cast效果一样,都安全;
对于下行转换,必须确定要转换的数据确实是目标类型的数据,即需要注意要转换的父类类型指针是否真的指向子类对象,如果是,static_cast和dynamic_cast都能成功;如果不是,static_cast能返回,但是不安全,可能会出现访问越界错误,而dynamic_cast在运行时类型检查过程中,判定该过程不能转换,返回NULL。
//父类 class Base { virtual void fun(){} }; //子类 class Derived:public Base { }; //dynamic_cast和static_cast都可以转换成功 Base *P = new Derived; //父类类型的指针指向子类对象 Derived *pd1 = static_cast<Derived *>(P); Derived *pd2 = dynamic_cast<Derived *>(P); /*static_cast编译时不会报错,也可以返回一个子类对象的指针,但运行时如果访问子类中有而父类中没有的数据或 函数成员,会因为访问越界而奔溃。*/ Base *P = new Base; //父类类型的指针指向父类对象 Derived *pd3 = static_cast<Derived *>(P); /*dynamic_cast会在运行时进行类型检查,返回NULL*/ Derived *pd4 = dynamic_cast<Derived *>(P);