Contents
5.4 类型转换高级 (Advacned Class Type-casting)
目前为止,我们一直使用传统的类型转换符来进行简单对象的类型转换。例如,要把一个double类型的浮点型数字转换为int 的整型数字,我们是这样做的:int i;
double d;
i = (int) d;
或者i = int (d);
这样做对基本数据类型时没问题的,因为基本数据类型的转换已经有标准的定义。同样的操作也可以被在类或类的指针上,因此以下例子中的写法也是没有问题的:
// class type-casting #include <iostream.h> class CDummy { int i; }; 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; } |
虽然以上程序在C++中是没有语法错误的(多数编译器甚至不会产生警告信息),但这段程序没有什么实际的逻辑意义。我们使用了CAddition 的成员函数result 而没有定义一个相应的该类的对象:padd 并不是一个对象,它只是一个指针,被我们赋值指向一个毫无关系的对象的地址。当在程序运行到访问它的result 成员函数时,将会有一个运行错误(run-time error)产生,或生成一个意外的结果。
为了控制这种类之间的转换,ANSI-C++ 标准定义了4种新的类型转换操作符: reinterpret_cast, static_cast, dynamic_cast 和 const_cast。所有这些操作符都是同样的使用格式:reinterpret_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
这里new_type 是要转换成的目标类型,expression 是要被转换的内容。为了便于理解,模仿传统转换操作符,它们的含义是这样的:(new_type) expression
new_type (expression)
reinterpret_cast
reinterpret_cast 可以将一个指针转换为任意其它类型的指针。它也可以用来将一个指针转换为一个整型,或反之亦然。
这个操作符可以在互不相关的类之间进行指针转换,操作的结果是简单的将一个指针的二进制数据(binary copy)复制到另一个指针。对指针指向的内容不做任何检查或转换。
如果这种复制发生在一个指针到一个整数之间,则对其内容的解释取决于不同的系统,因此任何实现都是不可移植(non portable)的。一个指针如果被转换为一个能够完全存储它的足够大的整数中,则是可以再被转换回来成为指针的。
例如:class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a);
reinterpret_cast 对所有指针的处理与传统的类型转换符所作的一模一样。
static_cast
static_cast 可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)。
用于类的指针,也就是说,它允许将一个引申类的指针转换为其基类类型(这是可以被隐含执行的有效转换),同时也允许进行相反的转换:将一个基类转换为一个引申类类型。
在后面一种情况中,不会检查被转换的基类是否真正完全是目标类型的。例如下面的代码是合法的:class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast(a);
static_cast除了能够对类指针进行操作,还可以被用来进行类中明确定义的转换,以及对基本类型的标准转换:double d=3.14159265;
int i = static_cast<int>(d);
译者注:如果你对这部分看不太懂,请结合下面的dynamic_cast一起看,也许会帮助理解。
dynamic_cast
dynamic_cast 完全被用来进行指针的操作。它可以用来进行任何可以隐含进行的转换操作以及它们被用于多态类情况下的方向操作。然而与static_cast不同的是, dynamic_cast 会检查后一种情况的操作是否合法,也就是说它会检查类型转换操作是否会返回一个被要求类型的有效的完整的对象。
这种检查是在程序运行过程中进行的。如果被转换的指针所指向的对象不是一个被要求类型的有效完整的对象,返回值将会是一个空指针NULL 。
class Base { virtual dummy(){}; };
class Derived : public Base { };
Base* b1 = new Derived;
Base* b2 = new Base;
Derived* d1 = dynamic_cast(b1); // succeeds Derived* d2 = dynamic_cast(b2); // fails: returns NULL
如果类型转换被用在引用(reference)类型上,而这个转换不可能进行的话,一个bad_cast 类型的例外(exception)将会被抛出:
class Base { virtual dummy(){}; };
class Derived : public Base { };
Base* b1 = new Derived;
Base* b2 = new Base;
Derived d1 = dynamic_cast(b1); // succeeds Derived d2 = dynamic_cast(b2); // fails: exception thrown
const_cast
这种类型转换对常量const 进行设置或取消操作:class C {};
const C * a = new C;
C * b = const_cast<C*> (a);
其他3种cast 操作符都不可以修改一个对象的常量属性(constness)。
typeid
ANSI-C++ 还定义了一个新的操作符叫做 typeid ,它检查一个表达式的类型:typeid (expression)
这个操作符返回一个类型为type_info的常量对象指针,这种类型定义在标准头函数中。这种返回值可以用操作符 == 和 != 来互相进行比较,也可以用来通过name()函数获得一个描述数据类型或类名称的字符串,例如:
// typeid, typeinfo #include <iostream.h> #include <typeinfo> class CDummy { }; int main () { CDummy* a,b; 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; } | a and b are of different types: a is: class CDummy * b is: class CDummy |