本文讨论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 = %x\n" ,
( int )pD); 04. CBaseY*
pY = pD; //
成功编译, pY = pD + 4 05. 06. printf ( "CBaseY*
pY = %x\n" ,
( int )pY); 07. void *
pV1 = pY; //成功编译,
pV1 = pY 08. 09. printf ( "void*
pV1 = %x\n" ,
( int )pV1); 10. //
pD2 = pY, 但是我们预期 pD2 = pY - 4 11. 12. CDerived*
pD2 = static_cast (pV1); 13. 14. printf ( "CDerived*
pD2 = %x\n" ,
( 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*。 |