看了一些网上的博客,整理了一些前辈们的思路,I am a burglar!端午节就这么过了,多年以后再回首吧。
命名的强制类型转换符号的一般形式如下:
cast_name<type>(expression);
其中, cast_name可以为dynamic_cast、static_cast、const_cast和reinterpret_cast的一种,type为转换的目标类型,而expression则是被强制转换的值。下面分别讲一下他们的具体含义。
1、const_cast
设定或去除型别的常数性,亦可去除volatile饰词,除此之外不允许任何转换:
(1)常量指针被转化成非常量指针,并且仍然指向原来的对象;
(2)常量引用被转换成非常量引用,并且仍然指向原来的对象;
(3)常量对象被转换成非常量对象。
#include<iostream> using namespace std; int main() { int num = 10; const int* ptr = # //int* ptr1 = ptr; //A int *ptr1 = const_cast<int*>(ptr) *ptr1 = 15; cout<<*ptr<<endl;; return 0; }A行的赋值将会产生编译错误,不能将const int*转换为int*
(1)static_cast用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。static_cast只能提供编译时的安全。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。(如果原先基类指针指向的是基类,那么这么做就会出错)。static_cast不能在兄弟之间进行转换。
(2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
(3)把空指针转换成目标类型的空指针;把任何类型的表达式转换成void类型。
范例1:
//class Der:public class Base //当pb确实指向一个Der,则这是使用两种转换方式都没问题 Base *pb = new Der(); Der *pd1 =static_cast<Der*>(pb); Der *pd2 =dynamic_cast<Der *>(pb); //当pb指向一个Base,则就有问题 Base *pb = newBase(); Der *pd1 =static_cast<Der*>(pb); //(1) Der *pd2 =dynamic_cast<Der *>(pb);//(2) //(1)此时pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的。 //(2)pd2将是一个空指针,即NULL指针。
//class Der:public class Base //class Der1:public class Base //static是无法在兄弟类之间完成转换的 Base *pb = new Der(); Der1 *pd1 = static_cast<Der1 *>(pb);//编译错误 Der1 *pd2 = reinterpret_cast<Der1 *>(pb);//可以完成 Der1 *pd3 = dynamic_cast<Der1 *>(pb);//可以完成 //pd2和pd3并不是真正转到相应的类型了
范例3:
#include<iostream> #include<cstdio> using namespace std; class CBaseX{ public: int x; CBaseX() { x = 10; } void foo() { printf("CBaseX::foo()x=%d\n", x); } }; class CBaseY{ public: int y; int* py; CBaseY() { y = 20; py = &y; } void bar() { printf("CBaseY::bar()y=%d, *py=%d\n", y, *py);} }; class CDerived :public CBaseX, public CBaseY{ public: int z; }; int main() { CDerived *pd = new CDerived(); //1 printf("CDerived* pd = %x\n",(int)pd); CBaseY *pb = pd; //2 printf("CBaseX* pb = %x\n",(int)pb); CDerived* pd1 =static_cast<CDerived*>(pb); //3 printf("CDerived* pd1 = %x\n",(int)pd1); CBaseY* pb2 =reinterpret_cast<CBaseY*>(pd); //4 printf("CBaseY* pb2 = %x\n",(int)pb2); CBaseY* pb3 = new CBaseY(); //5 printf("CBaseY* pb3 = %x\n",(int)pb3); CDerived* pd3 =static_cast<CDerived*>(pb3); //6 printf("CDerived* pd3 = %x\n",(int)pd3); CDerived* pd4 =reinterpret_cast<CDerived*>(pb3); //7 printf("CDerived* pd4 = %x\n", (int)pd4); return 0; }
运行结果:
针对上面的结果有如下分析:
//1:声明了一个指向派生类的指针
//2:一个简单的赋值,此时注意pb指向基类的指针,如果将派生类的指针赋给给它,此时pb只指向了派生类中基类对象的部分。
//3:此时将指向基类的指针转换为指向派生类的指针,注意它将pb的指针想前面移了4个位置,表示指向派生类。
//4:和第三行有同样的结果
//5:定义了一个指向基类的指针。
//6:这时还用static_cast转换,将指针往前面挪4个位置,但是由于本身就不存在派生类对象,这样访问前面的地址空间是不安全的。
//7:未做修改还是指向了原来的空间。
3、reinterpret_cast
C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。比如:
int i;
char *p = "This is aexample.";
i = reinterpret_cast<int>(p);
此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,//i 也是指针,整型指针;一个明显的现象是在转换前后没有数位损失。
范例:
int n = 9; double d = reinterpert_cast<dboule&>(n); cout<<n<<" "<<d; //输出:9 5.28421e-308 //因为reinterpert_cast仅仅是复制n的比特位到d,没有进行必要的分析。
4、dynamic_cast
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
如果是指针转换失败将返回NULL,如果是引用转换失败将抛出bad_cast.
dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
注:dynamic_cast要有虚函数,否则会编译出错;static_cast则没有这个限制。
注:dynamic_cast不支持反向转换。
范例1:
#include<iostream>
using namespace std;
class A{
public:
int m_nA;
};
class B{
public:
int m_nB;
};
class C:public A,public B{
public:
int m_nC;
};
int main()
{
C *pC = new C;
cout<<"pC= "<<(int) pC<<endl;
B *pB = dynamic_cast<B*>(pC); //a
B *pB1 = static_cast<B*>(pC); //b
B *pB2 = pC; //c
cout<<"pB= "<<(int) pB<<endl;
cout<<"pB1="<<(int) pB1<<endl;
cout<<"pB2="<<(int) pB2<<endl;
return 0;
}
运行结果:
分析:这是一个派生类指针向基类指针转换的例子,这种转换只是将派生类指针向下挪几个位置(挪多少与具体成员有关),即指向到基类位置的地方。因为在派生类中既包含基类成员又包含派生类成员。
范例2:
//一个常用的用法 void fun(Base* pB) { Der* pd = dynamic_cast<Der*>(pb); if(pd){......} }
范例3:
#include <iostream> using namespace std; class Base { public: virtual void f() { cout << "Base::f" << endl; } void f1(){cout << "Base::f1" << endl;} private: double x; double y; }; class Derived : public Base { public: virtual void f(){cout << "Derived::f" << endl; } virtual void k(){cout << "Derived::k" << endl; } private: double z; }; class Base1 { public: virtual void g(){ cout << "Base1::g" << endl;} void g1(){cout << "Base1::g1" << endl;} }; class Derived1 : public Base,public Base1 { public: virtual void f(){ cout << "Derived1::f" << endl;} virtual void h(){ cout << "Derived1::h" << endl;} }; void Test1() { // 对于单继承, // 如果pD真的指向Derived,用dynamic_cast和static_cast效果相同 Base *pD = new Derived; Derived *pD1 = dynamic_cast<Derived*>(pD); pD1->f(); pD1->k(); pD1->f1(); Derived *pD2 = static_cast<Derived*>(pD); pD2->f(); pD2->k(); pD2->f1(); // 但是如果pB不是真的指向Derived,则用dynamic_cast则返回NULL,能够更早的禁止error的发生, // 如果用static_cast虽然返回的不为NULL,但是运行时可能抛出exception。 /**///// Error code //Base *pB = new Base(); //Derived *pD3 = static_cast<Derived*>(pB); //pD3->f(); //pD3->k(); //pD3->f1(); //Derived *pD4 = dynamic_cast<Derived*>(pB); //pD4->f(); //pD4->k(); //pD4->f1(); } void Test2() { // 对于多重继承, // 如果pD真的指向的是Derived1,使用dynamic_cast和static_cast都可以转化为Derived1, // 但是如果要转化为Base的兄弟类Base1,必须使用dynamic_cast,使用static_cast不能编译。 Base *pD = new Derived1; Derived1 *pD1 = dynamic_cast<Derived1*>(pD); pD1->f(); pD1->h(); pD1->f1(); Base1 *pB1 = dynamic_cast<Base1*>(pD); pB1->g(); Derived1 *pD2 = static_cast<Derived1*>(pD); pD2->f(); pD1->h(); pD2->f1(); /**///// error can not compiler //Base1 *pB2 = static_cast<Base1*>(pD); //pB2->g(); // 当然对于pB不是真的指向Derived1,想要转化为Derived1或Base的兄弟类Base1,情况与Test1中的error情况相同。 } int main(int argc, _TCHAR* argv[]) { Test1(); Test2(); return 0; }