static_cast
const_cast
reinterpret_cast
运行时类型识别(RTTI)
dynamic_cast
哪种情况下dynamic_cast和static_cast使用的情况一样?
什么情况下使用dynamic_cast代替虚函数?
typeid
命名的强制类型转换形式如下:
cast_name<type>(expression);
其中:cast_name指static_cast、dynamic_cast、const_cast、reinterpret_cast中的一种;
type指要转换的目标类型;
expression指要转换的值或表达式。
static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。同时,对于编译器无法自动执行的类型转换也非常有用。static_cast不能转换掉expression的const、volitale、或者__unaligned属性。没有运行时类型检查来保证转换的安全性。
它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换。如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
如:
【代码1】
1 2 3 |
|
const_cast:只改变运算对象的底层const性质,不改变表达式(运算对象)的类型。可用于增加/去除运算对象的const属性。同时,只能使用const_cast来进行更改const属性,其他任何形式的命名强制类型转换都会引起编译器错误。如:
【代码2】
1 2 3 4 5 |
|
const_cast常常用于函数重载的上下文中。如:
【代码3】
1 2 3 4 5 6 7 8 9 10 11 |
|
reinterpret_cast:C++ Primer中解释:通常为运算对象的位模式提供较低层次上的重新解释。不懂~~有如下例子:
【代码4】
1 2 |
|
在代码4中,必须牢记pc实际上指向的是一个int而不是字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。如:
1 |
|
谨记:使用reinterpret_cast是非常危险的!要想安全的使用,必须对涉及的类型和编译器实现转换的过程都非常了解。所以,还是尽量不要使用的好!
同时,所有的强制类型转换,能不使用的情况尽量不要使用,因为其干扰了正常的类型检查。
运行时类型识别(RTTI):(由两个运算符实现)
typeid运算符,用来返回表达式的类型;
dynamic_cast运算符,用于将基类的指针或引用安全的转换成派生类的指针或引用。
这两个运算符特别适用于以下情况:使用‘基类对象’的‘指针或引用’执行某个‘派生类操作’并且’该操作‘不是‘虚函数’。
dynamic_cast运算符的使用形式:
1 2 3 4 |
|
其中,e的类型必须满足以下三个条件中的任一个:
- e的类型是目标type的公有派生类;
- e的类型是目标type的公有基类;
- e的类型是目标type的类型。
如果不满足上述三个中的任一个条件,则转换失败。同时,当使用dynamic_cast对一个空指针执行转换时,结果是所需类型的空指针。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。如:
【代码5】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
如果对无继承关系或者没有虚函数的对象指针进行转换、基本类型指针转换以及基类指针转换为派生类指针,都不能通过编译。
哪种情况下dynamic_cast和static_cast使用的情况一样?
【代码6】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
dynamic_cast可用于进行交叉转换:
【代码7】
1 2 3 4 5 6 7 8 9 10 |
|
dynamic_cast的讨论:
在探究 dynamic_cast 的设计意图之前,值得留意的是很多 dynamic_cast 的实现都相当慢。 例如,至少有一种通用的实现部分地基于对类名字进行字符串比较。如果你在一个位于四层深的单继承体系中的对象上执行 dynamic_cast,在这样一个实现下的每一个 dynamic_cast 都要付出相当于四次调用 strcmp 来比较类名字的成本。对于一个更深的或使用了多继承的继承体系,付出的代价会更加昂贵。
对 dynamic_cast 的需要通常发生在这种情况下:你要在一个你确信为派生类的对象上执行派生类的操作,但是你只能通过一个基类的指针或引用来操控这个对象。 有两个一般的方法可以避免这个问题:
- 使用存储着直接指向派生类对象的指针的容器,从而消除通过基类接口操控这个对象的需要。当然,这个方法不允许你在同一个容器中存储所有可能的基类的派生类的指针。为了与不同的窗口类型一起工作,你可能需要多个类型安全(type-safe)的容器。
- 通过一个基类的接口操控所有可能的 Window 派生类,就是在基类中提供一个让你做你想做的事情的虚函数。例如,尽管只有 SpecialWindows 能 blink,在基类中声明这个函数,并提供一个什么都不做的缺省实现或许是有意义的。
所以:避免强制转型的随意应用,特别是在性能敏感的代码中应用 dynamic_casts,如果一个设计需要强制转型,设法开发一个没有强制转型的侯选方案。 如果必须要强制转型,设法将它隐藏在一个函数中。客户可以用调用那个函数来代替在他们自己的代码中加入强制转型。
什么情况下使用dynamic_cast代替虚函数?
当基类代码不可知,需要在派生类里面新增新成员函数,但是又无法取得基类的源代码,不能在基类里面通过加虚函数接口调用新成员函数,可以通过dynamic_cast强制转换来获得调用。如:
【代码8】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
typeid运算符允许向表达式提问:“你的对象是什么类型?”
typeid(e),其中e可以是任意表达式或类型的名字。操作结果是一个常量对象的引用。如果表达式为一个引用,则typeid返回该引用所引用对象的类型。如果e为数组或函数,则不会执行向指针的标准类型转换,如数组,会返回数组类型。当运算对象不属于类类型或者是一个不包含任何虚函数的类时,typeid运算符返回的是e的静态类型。当e是定义了至少一个虚函数的类的左值时,typeid的结果直到运行时才能求得。
当typeid作用于指针p时,返回的是该指针的静态编译时类型;当作用于指针所指的对象(*p)时,要在运行时才能求得返回类型。同时,如果指针p所指的对象的类型不含有虚函数,则p可以为一个无效的指针。否则,指针所指的对象(*p)将在运行时求值,此时p必须为一个有效的指针,如p此时为一个空指针,则typeid(*p)会抛出std::bad_typeid异常。