C++ --> static_cast<> 和 reinterpret_cast<>

213 阅读4分钟
本文讨论static_cast<> 和 reinterpret_cast<>。介绍大多程序员在学C++前都学过C,并且习惯于C风格(类型)转换。当写C++(程序)时,有时候我们在使用static_cast<>和reinterpret_cast<>时可能会有点模糊。在本文中,我将说明static_cast<>实际上做了什么,并且指出一些将会导致错误的情况。泛型(Generic Types)01. float f = 12.3;``02.  03. float``* pf = &f;``04. // static cast<>``05.  06. // 成功编译, n = 12``07.  08. int n = ``static_cast``(f);``09.  10. // 错误,指向的类型是无关的(译注:即指针变量pf是float类型,现在要被转换为int类型)``11. //int* pn = static_cast(pf);``12.  13. //成功编译``14.  15. void``* pv = ``static_cast``(pf);``16.  17. //成功编译, 但是 *pn2是无意义的内存(rubbish)``18.  19. int``* pn2 = ``static_cast``(pv);``20. // reinterpret_cast<>``21.  22. //错误,编译器知道你应该调用static_cast<>``23.  24. //int i = reinterpret_cast(f);``25.  26. //成功编译, 但是 *pn 实际上是无意义的内存,和 *pn2一样``27.  28. int``* pi = ``reinterpret_cast``(pf);简而言之,static_cast<> 将尝试转换,举例来说,如float-到-integer,而reinterpret_cast<>简单改变编译器的意图重新考虑那个对象作为另一类型。指针类型(Pointer Types)指针转换有点复杂,我们将在本文的剩余部分使用下面的类:01. class CBaseX``02.  03. {``04.  05. public``:``06.  07. int x;``08.  09. CBaseX() { x = 10; }``10.  11. void foo() { ``printf``(``"CBaseX::foo() x=%d\n"``, x); }``12.  13. };``14. class CBaseY``15.  16. {``17.  18. public``:``19.  20. int y;``21.  22. int``* py;``23.  24. CBaseY() { y = 20; py = &y; }``25.  26. void bar() { ``printf``(``"CBaseY::bar() y=%d, *py=%d\n"``, y, *py);``27. }``28.  29. };``30. class CDerived : ``public CBaseX, ``public CBaseY``31.  32. {``33.  34. public``:``35.  36. int z;``37.  38. };情况1:两个无关的类之间的转换01. // Convert between CBaseX* and CBaseY*``02.  03. // CBaseX* 和 CBaseY*之间的转换``04.  05. CBaseX* pX = ``new CBaseX();``06.  07. // Error, types pointed to are unrelated``08.  09. // 错误, 类型指向是无关的``10.  11. // CBaseY* pY1 = static_cast(pX);``12.  13. // Compile OK, but pY2 is not CBaseX``14.  15. // 成功编译, 但是 pY2 不是CBaseX``16.  17. CBaseY* pY2 = ``reinterpret_cast``(pX);``18.  19. // System crash!!``20.  21. // 系统崩溃!!``22.  23. // pY2->bar();正如我们在泛型例子中所认识到的,如果你尝试转换一个对象到另一个无关的类static_cast<>将失败,而reinterpret_cast<>就总是成功“欺骗”编译器:那个对象就是那个无关类。情况2:转换到相关的类01. 1. CDerived* pD = ``new CDerived();``02.  03. 2. ``printf``(``"CDerived* pD = %x\n"``, (``int``)pD);``04.  05. 3.``06.  07. 4. ``// static_cast<> CDerived* -> CBaseY* -> CDerived*``08.  09. //成功编译,隐式static_cast<>转换``10.  11. 5. CBaseY* pY1 = pD;``12.  13. 6. ``printf``(``"CBaseY* pY1 = %x\n"``, (``int``)pY1);``14.  15. // 成功编译, 现在 pD1 = pD``16.  17. 7. CDerived* pD1 = ``static_cast``(pY1);``18.  19. 8. ``printf``(``"CDerived* pD1 = %x\n"``, (``int``)pD1);``20.  21. 9.``22.  23. 10. ``// reinterpret_cast``24.  25. // 成功编译, 但是 pY2 不是 CBaseY*``26.  27. 11. CBaseY* pY2 = ``reinterpret_cast``(pD);``28.  29. 12. ``printf``(``"CBaseY* pY2 = %x\n"``, (``int``)pY2);``30.  31. 13.``32.  33. 14. ``// 无关的 static_cast<>``34.  35. 15. CBaseY* pY3 = ``new CBaseY();``36.  37. 16. ``printf``(``"CBaseY* pY3 = %x\n"``, (``int``)pY3);``38.  39. // 成功编译,尽管 pY3 只是一个 "新 CBaseY()"``40.  41. 17. CDerived* pD3 = ``static_cast``(pY3);``42.  43. 18. ``printf``(``"CDerived* pD3 = %x\n"``, (``int``)pD3);``01. ---------------------- 输出 ---------------------------``02.  03. CDerived* pD = 392fb8``04.  05. CBaseY* pY1 = 392fbc``06.  07. CDerived* pD1 = 392fb8``08.  09. CBaseY* pY2 = 392fb8``10.  11. CBaseY* pY3 = 390ff0``12.  13. 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<>是没有区别的。情况3:void*之间的向前和向后转换因为任何指针可以被转换到void,而void可以被向后转换到任何指针(对于static_cast<> 和 reinterpret_cast<>转换都可以这样做),如果没有小心处理的话错误可能发生。01. CDerived* pD = ``new CDerived();``02.  03. printf``(``"CDerived* pD = %xn"``, (``int``)pD);``04. CBaseY* pY = pD; ``// 成功编译, pY = pD + 4``05.  06. printf``(``"CBaseY* pY = %xn"``, (``int``)pY);``07. void``* pV1 = pY; ``//成功编译, pV1 = pY``08.  09. printf``(``"void* pV1 = %xn"``, (``int``)pV1);``10. // pD2 = pY, 但是我们预期 pD2 = pY - 4``11.  12. CDerived* pD2 = ``static_cast``(pV1);``13.  14. printf``(``"CDerived* pD2 = %xn"``, (``int``)pD2);``15.  16. // 系统崩溃``17.  18. // pD2->bar();``01. ---------------------- 输出 ---------------------------``02.  03. CDerived* pD = 392fb8``04.  05. CBaseY* pY = 392fbc``06.  07. void``* pV1 = 392fbc``08.  09. 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