网站的基本元素,河北高端建设网站,机械网站建设方案,wordpress 工作原理大多程序员在学C前都学过C#xff0c;并且习惯于C风格#xff08;类型#xff09;转换。当写 C#xff08;程序#xff09;时#xff0c;有时候我们在使用static_cast和reinterpret_cast时可能会有点模糊。在本 文中#xff0c;我将说明static_cast… 大多程序员在学C前都学过C并且习惯于C风格类型转换。当写 C程序时有时候我们在使用static_cast和reinterpret_cast时可能会有点模糊。在本 文中我将说明static_cast实际上做了什么并且指出一些将会导致错误的情况。 泛型Generic Types float f 12.3; float* pf f; // static cast // 成功编译, n 12 int n static_castint(f); // 错误,指向的类型是无关的译注即指针变量pf是float类型现在要被转换为int类型 //int* pn static_castint*(pf); //成功编译 void* pv static_castvoid*(pf); //成功编译, 但是 *pn2是无意义的内存rubbish int* pn2 static_castint*(pv);// reinterpret_cast //错误,编译器知道你应该调用static_cast //int i reinterpret_castint(f); //成功编译, 但是 *pn 实际上是无意义的内存,和 *pn2一样 int* pi reinterpret_castint*(pf); 简而言之static_cast 将尝试转换举例来说如float-到-integer而reinterpret_cast简单改变编译器的意图重新考虑那个对象作为另一类型。 指针类型Pointer Types 指针转换有点复杂我们将在本文的剩余部分使用下面的类 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; }; 情况1两个无关的类之间的转换 // CBaseX* 和 CBaseY*之间的转换 CBaseX* pX new CBaseX(); // Error, types pointed to are unrelated // 错误 类型指向是无关的 // CBaseY* pY1 static_castCBaseY*(pX); // Compile OK, but pY2 is not CBaseX // 成功编译, 但是 pY2 不是CBaseX CBaseY* pY2 reinterpret_castCBaseY*(pX); // System crash!! // 系统崩溃!! // pY2-bar(); 正如我们在泛型例子中所认识到的如果你尝试转换一个对象到另一个无关的类static_cast将失败而reinterpret_cast就总是成功“欺骗”编译器那个对象就是那个无关类。 情况2转换到相关的类 1. CDerived* pD new CDerived(); 2. printf(CDerived* pD %x\n, (int)pD); 3. 4. // static_cast CDerived* - CBaseY* - CDerived* //成功编译隐式static_cast转换 5. CBaseY* pY1 pD; 6. printf(CBaseY* pY1 %x\n, (int)pY1); // 成功编译, 现在 pD1 pD 7. CDerived* pD1 static_castCDerived*(pY1); 8. printf(CDerived* pD1 %x\n, (int)pD1); 9. 10. // reinterpret_cast // 成功编译, 但是 pY2 不是 CBaseY* 11. CBaseY* pY2 reinterpret_castCBaseY*(pD); 12. printf(CBaseY* pY2 %x\n, (int)pY2); 13. 14. // 无关的 static_cast 15. CBaseY* pY3 new CBaseY(); 16. printf(CBaseY* pY3 %x\n, (int)pY3); // 成功编译,尽管 pY3 只是一个 新 CBaseY() 17. CDerived* pD3 static_castCDerived*(pY3); 18. printf(CDerived* pD3 %x\n, (int)pD3); ---------------------- 输出 --------------------------- CDerived* pD 392fb8 CBaseY* pY1 392fbc CDerived* pD1 392fb8 CBaseY* pY2 392fb8 CBaseY* pY3 390ff0 CDerived* pD3 390fec注 意在将CDerived*用隐式 static_cast转换到CBaseY*第5行时结果是指向CDerived*的指针向后 偏移了4个字节译注4为int类型在内存中所占字节数。为了知道static_cast 实际如何我们不得不要来看一下CDerived的内存布局。 CDerived的内存布局Memory Layout 如 图所示CDerived的内存布局包括两个对象CBaseX 和 CBaseY编译器也知道这一点。因此当你将CDerived* 转换到 CBaseY*时它给指针添加4个字节同时当你将CBaseY*转换到CDerived*时它给指针减去4。然而甚至它即便不是一个 CDerived你也可以这样做。当然这个问题只在如果你做了多继承时发生。在你将CDerived转换 到 CBaseX时static_cast 和 reinterpret_cast是没有区别的。情况3void*之间的向前和向后转换 因为任何指针可以被转换到void*而void*可以被向后转换到任何指针对于static_cast 和 reinterpret_cast转换都可以这样做如果没有小心处理的话错误可能发生。 CDerived* pD new CDerived(); printf(CDerived* pD %x\n, (int)pD); CBaseY* pY pD; // 成功编译, pY pD 4 printf(CBaseY* pY %x\n, (int)pY); void* pV1 pY; //成功编译, pV1 pY printf(void* pV1 %x\n, (int)pV1); // pD2 pY, 但是我们预期 pD2 pY - 4 CDerived* pD2 static_castCDerived*(pV1); printf(CDerived* pD2 %x\n, (int)pD2); // 系统崩溃 // pD2-bar(); ---------------------- 输出 --------------------------- CDerived* pD 392fb8 CBaseY* pY 392fbc void* pV1 392fbc CDerived* pD2 392fbc一旦我们已经转换指针为void*我们就不能轻易将其转换回原类。在上面的例子中从一个void* 返回CDerived*的唯一方法是将其转换为CBaseY*然后再转换为CDerived*。但是如果我们不能确定它是CBaseY* 还是 CDerived*这时我们不得不用dynamic_cast 或typeid[2]。 注释1. dynamic_cast从另一方面来说可以防止一个泛型CBaseY* 被转换到CDerived*。2. dynamic_cast需要类成为多态即包括“虚”函数并因此而不能成为void*。参考1. [MSDN] C Language Reference -- Casting2. Nishant Sivakumar, Casting Basics - Use C casts in your VC.NET programs3. Juan Soulie, C Language Tutorial: Type Casting 摘自http://blog.csdn.net/jiangdf/archive/2009/05/21/4205481.aspx dynamic_cast // Convert between CBaseX* and CBaseY* 作为四个内部类型转换操作符之一的dynamic_cast和传统的C风格的强制类型转换有着巨大的差别。除了 dynamic_cast以外的转换其行为的都是在编译期就得以确定的转换是否成功并不依赖被转换的对象。而dynamic_cast则不然。在这 里不再讨论其他三种转换和C风格的转换。首先dynamic_cast依赖于RTTI信息其次在转换时dynamic_cast会检查转换的source对象是否真的可以转换成target类型这种检查不是语法上的而是真实情况的检查。先 看RTTI相关部分通常许多编译器都是通过vtable找到对象的RTTI信息的这也就意味着如果基类没有虚方法也就无法判断一个基类指针变量 所指对象的真实类型, 这时候dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与.也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的.下面看一个例子:struct B1{ virtual ~B1(){}};struct B2{ virtual ~B2(){}};struct D1 : B1, B2{};int main(){ D1 d; B1* pb1 d; B2* pb2 dynamic_castB2*(pb1);//L1 B2* pb22 static_castB2*(pb1); //L2 return 0;}上述定义中可以看到,B1和B2是不相关的类,从L1可以看到,dynamic_cast允许这种转换:只要B1存在多态方法.L2将编译失败,static_cast并不允许两个完全不相干的类互相转换.dynamic_cast的这种特性在提取一个对象的某个接口的时候非常有用它很类似于实现了COM的QueryInterface的功能。正好在网上看到一个讲解强制转型的文章http://www.xker.com/article/articleview/2005-8-23/article_view_2732.htm文中这样描述dynamic_cast 主要用于执行“安全的向下转型safe downcasting”也就是说要确定一个对象是否是一个继承体系中的一个特定类型。这个描述是不完整的dynamic_cast 固然可以实现完全的向下转型也可以实现更为强大的QueryInterface的功能。