标签:
翻译:http://www.cplusplus.com/doc/tutorial/typecasting/
当一个值赋给另一个兼容的类型时,隐式转换自动执行,例如:
short a=2000; int b; b=a;
此处a的值从short转化int而不需要任何显示的操作符,此为标准转换。 标准转化影响基本数据类型,并且允许数字类型间的转化 (short
到int
, int
到float
, double
到int
...), 到或者从bool
, 及一些指针转化。
将较小的整数类型转化为int, 或将float转化为double即所谓的升级, 能够保证转化为目标类型后保存精确的数值, 其他类型间的转化可能无法保持准确的数值:
-1
将变成最大的整数,-2
第二大,...).bool的转化将
false
视为0 (对于数字类型) 或null pointer (对于指针类型); true
等效于其他所有值并转化为1
.一些这样的转化可能会丢失精度,编译器会给出警告,可通过显示转化避免这些警告。
对于非基本类型如数组和函数隐式转化为指针,允许以下转换:
void
pointers.对于类,其隐式转化通过以下三个成员函数实现:
例如:
// implicit conversion of classes: #include <iostream> using namespace std; class A {}; class B { public: // conversion from A (constructor): B (const A& x) {} // conversion from A (assignment): B& operator= (const A& x) {return *this;} // conversion to A (type-cast operator) operator A() {return A();} }; int main () { A foo; B bar = foo; // calls constructor bar = foo; // calls assignment foo = bar; // calls type-cast operator return 0; }
类型转化操作符使用了一种特殊的语法:它使用operator
关键字,紧跟着目标类型和一个空的括号。注意返回类型时目标类型并且不需要在关键字operator前面指定。
在函数调用中,C++每个参数的隐式转化。对于类有时是个问题,因为并不总是故意这样做的。例如在最后一个例子中添加函数:
void fn (B arg) {}
该函数接受类型为B的参数,但同时也可以接受类型为A的参数
fn (foo);
这也许是也许不是想要的结果。但无论如何可以在构造函数上使用explicit关键字避免这种行为:
// explicit: #include <iostream> using namespace std; class A {}; class B { public: explicit B (const A& x) {} B& operator= (const A& x) {return *this;} operator A() {return A();} }; void fn (B x) {} int main () { A foo; B bar (foo); bar = foo; foo = bar; // fn (foo); // not allowed for explicit ctor. fn (bar); return 0; }
此外被explicit标志的构造函数不能被用来调用参数类的语法
;在以上例子中,bar不能使用以下方式构造:
B bar = foo;
类型转化成员函数 (上一节所描述的函数) 也可以指定为explicit
。这将类似explicit的构造函数那样防止目的类型的隐式转换。
C++是一种强类型语言。许多转换,特别是意味着解释为一个不同的值,要求显示的转换,这就是C++的类型转换(type-casting),通用的类型转换有两种语法:函数和类C:
double x = 10.3; int y; y = int (x); // functional notation y = (int) x; // c-like cast notation
这些通用的转换形式对于基本的数据类型已经足够,然而这些操作符会被无区分地应用到类和指向类的指针,这可能导致——尽管语法正确,到会有运行时错误。例如以下代码无编译错误:
// class type-casting #include <iostream> using namespace std; class Dummy { double i,j; }; class Addition { int x,y; public: Addition (int a, int b) { x=a; y=b; } int result() { return x+y;} }; int main () { Dummy d; Addition * padd; padd = (Addition*) &d; cout << padd->result(); return 0; }
这段程序声明了一个指向Addition的指针,
但然后通过显示转换赋给它一个指向不想管类的指针:
padd = (Addition*) &d;
无限制的隐式转换允许将指向任何类型的指针转换为其他类型的指针,而与它们所指向的类型独立。接下来的成员调用 result
导致一个运行时错误或者一些未知结果。
为了控制这种类之间的类型转换,我们有四种特定的类转换操作符: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)
等效于这些表达式的传统类型转换是:
(new_type) expression
new_type (expression)
然而每一种都有各自的特征。
dynamic_cast
只能应用到类的指针或引用 (或void*
)。其目的是保证类型转换的结果指向一个有效的完整的目标类型的类。
这自然的包含了指针向上转换 (pointer upcast,从pointer-to-derived到pointer-to-base转换),像隐式转换那样的方式。
但dynamic_cast
也可以向下转换(downcast ,从pointer-to-base到pointer-to-derived转换) 为多态类 (具有虚函数),当且仅当所指向的类是一个指向目标类型的有效的完整的类。例如:
// dynamic_cast #include <iostream> #include <exception> using namespace std; class Base { virtual void dummy() {} }; class Derived: public Base { int a; }; int main () { try { Base * pba = new Derived; Base * pbb = new Base; Derived * pd; pd = dynamic_cast<Derived*>(pba); if (pd==0) cout << "Null pointer on first type-cast.\n"; pd = dynamic_cast<Derived*>(pbb); if (pd==0) cout << "Null pointer on second type-cast.\n"; } catch (exception& e) {cout << "Exception: " << e.what();} return 0; }
Compatibility note: This type of dynamic_cast requires Run-Time Type Information (RTTI) to keep track of dynamic types. Some compilers support this feature as an option which is disabled by default. This needs to be enabled for runtime type checking using dynamic_cast to work properly with these types. |
以上的代码尝试执行两个dynamic casts,从Base*
(pba
and pbb
)到Derived*
,但只有第一个成功,注意它们各自的初始化:
Base * pba = new Derived; Base * pbb = new Base;
尽管两者类型都是Base*
,但 pba
事实上指向类型为Derived的对象
,而pbb
指向类型为Base的对象。当各自执行
ynamic_cast时,
pba
指向一个完整的Derived对象,
而pbb
指向类型为Base的对象
,是一个不完整的Derived对象。
当dynamic_cast
由于指针所指的不是一个完整的目标类型的对象,从而无法转换 -如前面例子的第二个转换- 它会返回一个空指针表示转换失败。如果dynamic_cast
用来转换一个引用类型并且转换失败,一个类型为bad_cast
的异常会被抛出。dynamic_cast
也可以执行其他指针间的隐式转换:在指针类型间(即便在不相关的类间)转换null pointers,以及将指向任何类型的任何指针转换为 void*
指针。
static_cast
执行指向相关类的指针间的转换,不仅向上转换(从pointer-to-derived到pointer-to-base),还包括向下转换(从pointer-to-base到pointer-to-derived)。在运行时不进行检查以保证正被转换的对象是目标类型的一个完整对象。因此由程序员负责保证转换的安全性。另一方面,它不会引起dynamic_cast类型安全检查的结果
。
class Base {}; class Derived: public Base {}; Base * a = new Base; Derived * b = static_cast<Derived*>(a);
以上是有效的代码,因此static_cast
执行类指针间的转换,既包括隐式允许的,也包括相反的转换。它能
void*
为任何指针类型。在这种情况,它保证:如果void*
的值是通过将同样的指针类型转换得到的,那么结果的指针值将是一样的。此外,static_cast
能够执行:
void
,评估或丢弃该值 reinterpret_cast
将任何指针类型转换为其他的任何指针类型,即使是不相关的类。该操作的结果是从一种类型到另一种类型的二进制复制。所有指针转换都允许:指针所指的内容或者指针本身都不被检查。
它也能用来在整数和类指针间转换,整数值表示指针的格式是平台特定的。唯一的保证是一个指针转换为一个足够表示它的整数类型后(例如 intptr_t
),能够转换回一个有效的指针。
能够由reinterpret_cast
而不能由static_cast执行的操作是
基于重新解释类型二进制表示的低级操作,这种解释通常会导致系统特定的结果,因此缺乏可移植性。例如:
class A { /* ... */ }; class B { /* ... */ }; A * a = new A; B * b = reinterpret_cast<B*>(a);
该代码能通过编译,尽管看上去讲不太通,因为如今b
指向一个完全不相关的类型并且可能不兼容的对象,对b解引用
是不安全的。
这种转换控制指针所指对象的const属性,设置或者移除之。例如,为了传递一个const对象给一个接受非const对象的函数:
// const_cast #include <iostream> using namespace std; void print (char * str) { cout << str << ‘\n‘; } int main () { const char * c = "sample text"; print ( const_cast<char *> (c) ); return 0; }
以上的代码能够正确运行,因为函数print
没有写到所指的对象。然而注意,移除所指对象的const属性用以写操作将导致无定义行为。behavior.
typeid允许检查一个表达式的类型:
typeid (expression)
该操作符返回一个类型为type_info
的常对象的引用,type_info定义在标准头文件<typeinfo>
中。由typeid返回的值可以用来和
另一个typeid返回的值通过操作符
==
和!=
进行比较或者可以通过它的name()成员获得表示数据类型或类类型的null结尾的字符串。
// 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
应用到类时,typeid使用
RTTI跟踪动态对象的类型,当typeid
被应用到一个表达式,其类型为多态类,则结果为最完整的继承类:
// typeid, polymorphic class #include <iostream> #include <typeinfo> #include <exception> using namespace std; class Base { virtual void f(){} }; class Derived : public Base {}; int main () { try { Base* a = new Base; Base* b = new Derived; 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() << ‘\n‘; } return 0; }
注意:type_info的成员
name返回的字符串依赖于编译器和库特定的执行。
注意到typeid
将指针的类型视为指针本身的类型 (a
和b的类型为
Base *
)。然而,当typeid
应用到对象(如*a
和*b
) typeid
得到的是动态类型 (即最完整的继承类类型).
如果typeid
评估的类型是一个指针前带解引用符号*,并且该指针具有null值,则typeid
抛出bad_typeid
异常.
标签:
原文地址:http://www.cnblogs.com/cai2007/p/4593248.html