它告诉编译器:“想象ClassName
在 0x10000000 处有一个对象。相对于 0x10000000,该对象中的数据将从哪里BaseName
开始”?
考虑具有多个基的类对象的内存布局:
class A: B, C{};
在构成 A 对象的内存块中,有属于 B 的数据块,也有属于 C 的数据块,以及特定于 A 的数据。由于至少有一个基数据的地址不能与整个类实例的地址相同,传递给不同方法的指针的数值this
需要有所不同。宏检索差值。
编辑:按照惯例,指向 vtable 的指针是任何具有虚函数的类中的第一个数据成员。因此,通过查找基本数据的地址,可以找到其 vtable 指针的地址。
现在,关于类型转换。通常,当您对指针进行类型转换时,该操作在内部是微不足道的——地址的数值不取决于它指向的类型;数据类型的概念只存在于 C 级别。但是有一个重要的例外 - 当您使用多重继承转换对象指针时。正如我们刚刚讨论过的this
,您需要传递给基类方法的指针可能在数值上与派生对象的指针不同。
因此 static_cast 和 reinterpret_cast 之间的区别巧妙地捕捉到了这种差异。当您使用 reinterpret_cast 时,您是在告诉编译器:“我知道得更好。取这个数值并将其解释为指向我所说内容的指针”。这是对类型系统的蓄意颠覆,很危险,但偶尔也是必要的。这种演员阵容根据定义是微不足道的——因为你这么说。
我所说的“微不足道”是指 - 指针的数值不会改变。
static_cast 是一个更高级别的构造。在这种特殊情况下,您在一个对象和它的基础之间进行转换。在 C++ 类规则下,这是一个合理、安全的强制转换——但它可能在数值上并不重要。这就是宏使用两种不同类型转换的原因。static_cast 不违反类型系统。
回顾一下:
reinterpret_cast<ClassName* >(OxlOOOOOOO)
是不安全的操作。它返回一个指向虚假对象的虚假指针,但这没关系,因为我们从不取消引用它。
static_cast<BaseName*>(...)
是一个安全的操作(具有不安全的指针,具有讽刺意味)。这是发生非平凡指针类型转换的部分。
(DWORD(...)-OxlOOOOOOO)
是纯算术。这就是不安全性加倍自身的地方:我们没有将指针用作指针,而是将其转换回整数并忘记它曾经是指针。
最后阶段可以等效地改写为:
((char*)(...)-(char*)OxlOOOOOOO)
如果这更有意义。