C++的类型转换包括了两类,隐式转换和显式转换。
隐式转换常见于两种情况,一种是基础类型运算时,自动从低精度类型向高精度类型转换,如char转int、int转double等。
int a = 100;
double b = a / 4.0;
另一种情况是对象之间赋值,隐式地调用了复制构造函数。
class A { };
class B { public: B(A a) { } };
A a;
B b = a;
显式转换,也可以叫做强制类型转换。C++是强类型编程语言,每个变量的类型必需是明确的。一般的转换如下:
double a = 10.0;
int b = (int) a; /* 精度损失 */
这种显式转换方式支持任意两个类型之间转换,但任意的转换可能会导致Runtime Error。
class A { public: int num; };
classB {
public:
void show() { cout << num << endl; }
int num;
};
A a;
B *b = (B *) &a;
b->show(); /* Error */
为了解决这个问题,C++包含四种Casting操作符,对转换的类型进行限制,基本语法如下:
dynamic_cast<new_type>(expression)
reinterpret_cast<new_type>(expression)
static_cast<new_type>(expression)
const_cast<new_type>(expression)
一般来说,对于一个好的设计,应该是尽量少用强制类型转换,这里从限制最严的Casting操作符开始说起。
dynamic_cast
该操作符允许类型在继承链上向上或者向下转换,子类指针向基类指针转换是必定可以转换成功的。事实上,这也是好的设计允许的,基类指针指向子类对象(多态条件之一)。
class Base { };
class Derived: public Base { };
Derived a;
Base *b = dynamic_cast<Base *>(&a);
而对于向下转换,该操作符会在运行时检测转换是否合法。好的设计应尽量避免向下转换。对于不合法的转换,dynamic_cast返回NULL。
Base *a = new Derived();
Base *b = new Base();
Derived *pa = dynamic_cast<Derived *>(&a);
Derived *pb = dynamic_cast<Derived *>(&b); /* 返回NULL */
该操作符还支持NULL指针转换为其他指针,以及其他指针转换为void *。
static_cast
和dynamic_cast类似,static_cast也只能在继承链上向上或向下转换,但条件比前者更宽松。在向下转换过程中,static_cast不会检测转换是否合法,即上例中dynamic_cast返回NULL,而static_cast则能成功转换。
除了和dynamic_cast类似的功能外,static_cast还能对非指针类型进行转换。该转换的实质与隐式转换类似,即基础类型的转换或者是实现了复制构造函数的对象间转换。
double a = 1.25;
int b = static_cast<int>(a);
reinterpret_cast
该操作符最为宽松,允许任意指针的转换,不要求对象之间是相关的。同时还支持从整型地址和指针之间的转换。
class A { };
class B { };
A a;
B *b = reinterpret_cast<B *>(a);
const_cast
以上三个操作符都无法实现const和非const之间转换,该操作符可以转换对象的const和validate属性(设置或取消)。
const int a = 100;
int *b = const_cast<int *>(&a);
typeid
该操作符可以获取对象的类型,在头文件typeinfo中定义。返回的结果是能够最准确描述对象的类型,例如传入基类指针(指向子类对象),返回的类型是子类。返回值可以直接用于比较,判断两个对象的类型是否相同。
#include <typeinfo>
Base *a = new Base();
Base *b = new Derived();
cout << tpyeinfo(a) == typeinfo(b) << endl; /* 不相等 */
cout << typeinfo(a).name << endl;
cout << typeinfo(b).name << endl;
/* name通常是一个可读字符串,与编译器有关 */