1

我没有看到下面的宏在做什么?如果有人可以帮助我看到它,将不胜感激。

#define BASE_OFFSET(ClassName,BaseName)\
(DWORD(static_cast < BaseName* >( reinterpret_cast\
< ClassName* >(Ox10000000)))-Ox10000000)

如果有人想知道它的来源,它来自 Don Box Book Essential COM 的第 3 章,他正在使用接口表构建 QueryInterface 函数,上面的宏以某种方式用于查找指向接口 vtable 的指针类,其中类是实现 BaseName 的 ClassName,虽然我不知道它是如何做到的。

4

2 回答 2

7

它告诉编译器:“想象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)

如果这更有意义。

于 2012-07-24T19:38:53.673 回答
1

关于魔术 0x10000000 常量的备注。

如果该常数为 0,则 GCC 将显示警告 -Winvalid-offset-of(当然,如果已启用)。也许其他编译器会做类似的事情。

于 2013-04-22T14:30:39.607 回答