前言
原文翻译自http://www.cplusplus.com/doc/tutorial/typecasting/,觉得这篇文章讲C++类型转换简单明了,所以特别翻译了下。
在C++中,将一个已知的类型转换为另一个类型,我们称呼为类型转换,本文会介绍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 notation
b = 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-base
pd = 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异常。