我没看到下面的宏在做什么?如果有人能帮助我看到它,将不胜感激。
#define BASE_OFFSET(ClassName,BaseName)\
(DWORD(static_cast < BaseName* >( reinterpret_cast\
< ClassName* >(Ox10000000)))-Ox10000000)
如果有人想知道它来自哪里,它来自Don Box Book Essential COM的第3章,他正在使用接口表构建QueryInterface函数,上面的宏以某种方式用于查找指针类的接口vtable,其中class是实现BaseName的ClassName,虽然我不知道它是如何做的。
答案 0 :(得分:7)
告诉编译器:“想象一下ClassName
对象位于0x10000000。BaseName
数据在该对象中的哪一个开始,相对于0x10000000”?
考虑具有多个碱基的类对象的内存布局:
class A: B, C{};
在构成A对象的内存块中,存在属于B的数据块,也是属于C的数据块,以及特定于A的数据。由于至少一个base的地址数据不能与作为整体的类实例的地址相同,传递给不同方法的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)
如果这更有意义。
答案 1 :(得分:1)
备注魔法0x10000000常数。
如果该常量为0,GCC将显示警告-Winvalid-offset-of(如果它已启用,当然)。也许其他编译器会做那样的事情。