C++中的static_cast, dynamic_cast和reinterpret_cast
郑重声明:本文是笔者根据个人理解所写,错误难免,欢迎拍砖!
可以任意转载、修改,转载时是否标明出处,随君而定!
static_cast
用于“良性”和“适度良性”转换,包括不用强制转换(如自动类型转换)。static_cast全部用于明确定义的转换,包括编译器允许我们所做的不用强制转换的“安全”转换和不太安全但清楚定义的转换。
用法:static_cast < type-id > ( expression)
该运算符把expression转换为type-id类型,但没有在运行时进行类型检查来保证转换的安全性。static_cast包含的类型转换包括一下几种:
A、典型的非强制变换
B、窄化(有信息丢失)变换
C、使用void*的强制变换
D、隐式类型变换
E、类层次的静态定位
a. 向上转换是安全的,即将子类的指针或引用转换为基类
b. 向下转换是不安全的,即将基类指针或引用转换成子类,因为是静态转换,没有在运行时进行类型检查,应使用dynamic_cast。
1 int i = 0x7fff; // max pos value = 32767 2 long l = 0; 3 float f = 1.0f; 4 5 // A: typeical castless conversions: 6 l = static_cast<long>(i); 7 8 // B: narrowing conversions: 9 i = static_cast<int>(f); 10 11 // C: forcing a conversion from void* : 12 void* pvoid = &i; 13 float* pf = static_cast<float*>(pvoid); 14 15 // D: Implicit type conversions, normally performed by the compiler 16 double d = 0.0; 17 int x = d; // Automatic type conversion 18 x = static_cast<int>(d); // More explicit
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
dynamic_cast
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
当使用dynamic_cast来试着向下类型转换一个特定的类型,仅当类型转换是争取的并且是成功的时,返回值是一个指向所需类型的指针,否则它将返回0来表示转换失败。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
1 #include <iostream> 2 using namespace std; 3 4 class CPet { public : virtual ~CPet() {} }; 5 class CDog : public CPet {}; 6 class CCat : public CPet {}; 7 8 int main() 9 { 10 CPet* pPet = new CCat; // Upcast, safe 11 // Try to cast it to CCat* : 12 CCat* pCat = dynamic_cast<CCat*>(pPet); // cast success 13 // Try to cast it to CDog* : 14 CDog* pDog = dynamic_cast<CDog*>(pPet); // cast fail 15 16 cout << "pDog = " << (long)pDog << endl; 17 cout << "pCat = " << (long)pCat << endl; 18 }
在上面的代码段中,将pPet向下转换为CCat*类型成功了(因为CPet* pPet = new CCat;),转换为CDog*类型失败了,说明dynamic_cast会进行类型检测。
当使用dynamic_cast时,必须对一个真正多态的层次进行操作(即它含有虚函数),这是因为dynamic_cast使用了存储在VTABLE中的信息来判断实际的类型。
注意:无论何时进行向下类型转换,我们都应该进行类型检查以确保转换是成功的(即返回值非0)。但我们不用确保指针要完全一样,因为通常在向上和向下类型转换时指针会进行调整(特别是多重继承的情况下)。
dynamic _cast运行时需要一点额外的开销,不多,但是执行大量的dynamic_cast时就说明我们的设计有问题了,也会导致性能损失。在进行向下转换时,如果我们已经明确知道是什么类型,就可以使用static_cast,以避免调用dynamic_cast产生的额外开销。比如上面的将pPet向下转换为CCat*类型,可以这样写:
CCat* pCat = static_cast<CCat*>(pPet);
reinterpret_cast
这是最不安全的一种转换机制,最有可能出问题。reinterpret_cast把对象假象为模式(为了某种隐秘的目的),仿佛它是一个完全不同的类型对象。这是低级的位模式,C因此而名称不佳。在使用reinterpret_cast做任何事之前,实际上总是需要reinterpret_cast回到原来的类型(或者把变量看做是它原来的类型)。说白了,就是将原来的类型隐藏起来,用的时候再转换回去,否则编译失败。
用法:reinterpret_cast< type-id > (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。
const_cast
const_cast用来修改类型的const或volatile属性,即可以将const转换为非const以及volatile转换为非volatile,这是const_cast唯一允许的转换。除了const 或volatile修饰之外, type_id和exdivssion的类型是一样的。
用法:const_cast< type-id > (expression)
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
Voiatile和const类试。举如下一例:
1 class B 2 { 3 public: 4 int m_iNum; 5 } 6 void foo() 7 { 8 const B b1; 9 b1.m_iNum = 100; //comile error 10 B b2 = const_cast<B>((b1); 11 b2. m_iNum = 200; //fine 12 }
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;
使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。
简单总结一下:
dynamic_cast: 通常在基类和派生类之间转换时使用;
const_cast: 主要针对const和volatile的转换.
static_cast: 一般的转换,如果你不知道该用哪个,就用这个。
reinterpret_cast: 用于进行没有任何关联之间的转换,比如一个字符指针转换为一个整形数。
参考资料: