最近正好遇到了cast…就顺便查了一下C++下的四种cast的办法,顺便算个小记录吧
reference:http://www.cplusplus.com/doc/tutorial/typecasting/
dynamic_cast
dynamic_cast 只能够用在指向类的指针或者引用上(或者void*)。这种转换的目的是确保目标指针类型所指向的是一个有效且完整的对象。
同隐式转换一样,这种转换允许upcast(从派生类向基类的转换)。
但dynamic_cast 也能downcast(从基类向派生类的转换)当且仅当转过去的指针所指向的目标对象有效且完整。例如:
// 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;
}
输出:
Null pointer on second type-cast.
显然第二次pd所指向的对象并不完整。
当发生如上情况时,会返回空指针来表示转换失败。倘若是无法完成的引用转换,则会抛出bad_cast 异常。
此外 dynamic_cast 也能够完成空指针在任意指针类型上的转换,以及任意指针类型向void*的转换。
注意:在上面例子中的dynamic_cast 需要用到RTTI(Run-Time Type Identification);在一些支持此特性但是默认是关闭的编译器上需要额外打开。
static_cast
static_cast能够完成指向相关类的指针上的转换。upcast和downcast都能够支持,但不同的是,并不会有运行时的检查来确保转换到目标类型上的指针所指向的对象有效且完整。因此,这就完全依赖程序员来确保转换的安全性。但反过来说,这也不会带来额外检查的开销。例如
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
虽然b指向的并不是一个完整的类以及后续在使用b时可能导致runtime error,这却是一段合法的代码。
同时,static_cast能够完成所有隐式类型转换以及它们的相反转换,更具体点:
- void*转向任何指针类型;假如该指针是从某指针类型上转到void *,现在再次转到相同的指针类型上去的话,保证转换后的指针本身的值不变。
- 整数,浮点以及枚举类型向枚举类型的转换。
此外,还能够完成:
- 显式调用仅有一个参数的构造函数或者是转换操作符(conversion operator)
- 转换到右值引用
- 枚举类的值向整数或浮点的转换
- 任意类型向void的转换,目标值在被求值之后舍弃。
reinterpret_cast
reinterpret_cast能够完成任意指针类型向任意指针类型的转换,即使它们毫无关联。该转换的操作结果是出现一份完全相同的二进制复制品,既不会有指向内容的检查,也不会有指针本身类型的检查。
reinterpret_cast也能够完成指针向整数的转换。不过该整数的类型取决于平台。唯一的保证是该整数的类型能够容纳下指针的值以及能够再次被转换成一个有效的指针。
基本上reinterpret_cast能做但static_cast不能做的转换大多都是一些基于重新解释二进制的底层操作,因此会导致代码限定于特定的平台进而导致差移植性。例如:
class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);
这个例子将A指向的二进制块当做B来重新解释。虽然可以编译,但是没有什么用处。A与B是完全没有关联的类而且看起来也不兼容。同时,对B进行解引用操作并不安全。
const_cast
const_cast可以用来设置或者移除指针所指向对象的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;
}
输出:
sample text
注意,在移除const之后假如真的向目标进行写操作将导致UB。
typeid
typeid用来检查表达式的类型。
typeid (expression)
这个操作会返回一个定义在里面的const对象,这个对象可以同其他用typeid获取的const对象进行==或者!=操作,也可以用过.name()来获取一个表示类型名或者是类名的以空字符结尾的字符串。例如:
// 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;
}
输出:
a and b are of different types:
a is: int *
b is: int
当作用在类上时,要用到RTTI来维护动态对象的类型信息。假如参数表达式的类型是多态类,结果将会是派生得最完整的类,例如:
// 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;
}
输出:
a is: class Base *
b is: class Base *
*a is: class Base
*b is: class Derived
注意:
- 当作用在指针上时,仅会考虑其本身类型。
- 当作用在类上时,才会产生出动态类型,即结果是派生得最完整的类。
- 倘若传入的是解引用的空指针,则会抛出bad_typeid异常。
此外,name()的实现依赖于编译器及其使用的库,也许并不一定是单纯的类型名。