C++类型转换
将一个已知的类型转换为另一个类型,我们称呼为类型转换
隐式转换
隐式转换不需要任何操作符,它们会自动执行,当值被赋值到兼容类型,就会执行,例如:
short a=2000;int b;b=a;复制代码隐式转换,也包括构造函数和运算符的转换,例如:
class A {};class B { public: B (A a) {} };A a;B b=a;复制代码
显式转换
C++是一个强类型的语言。许多转换,需要显式转换,例如
short a=2000;int b;b = (int) a; // c-like cast notationb = int (a); // functional notation复制代码
上述的类型转换已经满足了基本类型的转换了,但是如果应用于类和指针中,代码可以编译,但是在运行过程中会出错。例如
// class type-casting#include <iostream>using namespace std;class CDummy { float i,j;};class CAddition { int x,y; public: CAddition (int a, int b) { x=a; y=b; } int result() { return x+y;}};int main () { CDummy d; CAddition * padd; padd = (CAddition*) &d; cout << padd->result(); return 0;}复制代码这段代码会在运行期出错,在执行padd->result()时异常退出。
传统明确的类型转换,可以转换成任何其他指针类型任何指针,它们指向的类型无关。在随后的调用成员的结果,会产生一个运行时错误或意外的结果。
C++标准转换运算符
传统的类和指针的类型转换,十分不安全,可能会在运行时,由于类型不匹配异常退出,所以C++提供了四个标准转换运算符:dynamic_cast, reinterpret_cast, static_cast, const_cast
dynamic_cast <new_type> (expression)reinterpret_cast <new_type> (expression)static_cast <new_type> (expression)const_cast <new_type> (expression)复制代码
dynamic_cast
dynamic_cast只能用于指针和引用的对象。其目的是确保类型转换的结果是一个有效的完成所请求的类的对象,所以当我们从一个类转换到这个类的基类,dynamic_cast总是可以成功
class CBase { };class CDerived: public CBase { };CBase b; CBase* pb;CDerived d; CDerived* pd;pb = dynamic_cast<CBase*>(&d); // ok: derived-to-basepd = dynamic_cast<CDerived*>(&b); // wrong: base-to-derived复制代码当新的类型不是被转换的类型的基类,dynamic_cast无法完成指针的转换,返回NULL,如果dynamic_cast是用来转换为引用类型的转换失败,会抛出"Bad_cast exception"异常。
dynamic_cast 可以转换NULL指针为不相关的类,也可以任何类型的指针为void指针。
dynamic_cast的需要的运行时类型信息(RTTI),保持动态类型跟踪。一些编译器支持此功能默认情况下是禁用的选项。这必须启用运行时类型检查,使用dynamic_cast的正常工作。
static_cast
static_cast可以执行相关的类的指针之间的转换,不仅从派生类到基类的转换,也可以做到基类到派生类的转换。static_cast没有在运行时进行安全检查,因此,它是程序员,以确保转换是安全的,但是dynamic_cast的类型安全检查的开销,static_cast是可以避免的。
class CBase {};class CDerived: public CBase {};CBase * a = new CBase;CDerived * b = static_cast<CDerived*>(a);复制代码上述代码是合法的,虽然b指向一个不完整的对象,并可能在运行期导致错误。
static_cast也可以用来执行任何其他非指针的转换,例如像基本类型之间的标准转换,也可以是隐式执行
double d=3.14159265;int i = static_cast<int>(d); 复制代码
reinterpret_cast
reinterpret_cast转换成任何其他指针类型,甚至无关的类,任何指针类型。操作的结果是一个简单的从一个指针到其他的值的二进制拷贝。所有的指针转换是允许的:不管是指针指向的内容还是指针本身的类型。
同时,它也可以把指针转换为整数,但是整数是平台相关的,必须保证整数足够大到可以包含指针本身的内容,最后再转换为一个合法的指针。
class A {};class B {};A * a = new A;B * b = reinterpret_cast<B*>(a)复制代码
const_cast
const_cast用于操纵对象的常量性,既要设置或删除。例如,一个函数要求一个非const参数,而程序需要传递一个const参数。
#include <iostream>using namespace std;void print (char * str){ cout << str << endl;}int main () { const char * c = "sample text"; print ( const_cast<char *> (c) ); return 0;}复制代码
typeid
typeid的允许检查表达式的类型
typeid (expression)
这个操作符返回一个引用在标准头文件<typeinfo>中定义的常量对象,是一个类型的type_info。这个返回值可以与另一个使用运算符==和!=进行比较两个数据类型或类的名称,或者也可以使用其name() 成员函数获得类型名字(一个0结束的的字符串)。
// typeid#include <iostream>#include <typeinfo>using namespace std;int main () { int * a,b; a=0; b=0; if (typeid(a) != typeid(b)) { cout << "a and b are of different types:\n"; cout << "a is: " << typeid(a).name() << '\n'; cout << "b is: " << typeid(b).name() << '\n'; } return 0;}复制代码当typeid应用使用RTTI来跟踪动态对象的类型,那么当typeid的是应用于表达式,其类型是一个多态类,其结果是派生的最完整的对象的类型:
// typeid, polymorphic class#include <iostream>#include <typeinfo>#include <exception>using namespace std;class CBase { virtual void f(){} };class CDerived : public CBase {};int main () { try { CBase* a = new CBase; CBase* b = new CDerived; cout << "a is: " << typeid(a).name() << '\n'; cout << "b is: " << typeid(b).name() << '\n'; cout << "*a is: " << typeid(*a).name() << '\n'; cout << "*b is: " << typeid(*b).name() << '\n'; } catch (exception& e) { cout << "Exception: " << e.what() << endl; } return 0;}复制代码注意:返回的字符串成员的type_info名称取决于你的编译器和库的具体实现,其典型的类型名称,它不一定是一个简单的字符串.
如果类型typeid的参数是引用操作符(*)开头的指针,而且这个指针是NULL,typeid会抛出一个bad_typeid