【发布时间】:2011-09-18 19:58:36
【问题描述】:
我有一些代码实现了一种运行时反射。为了在给定实例中获取指向类字段的指针,我基本上采用指向类实例的指针并添加一个固定偏移量,该偏移量为暴露给反射库的每个字段计算一次。
我的实现非常简单,因为我不需要支持多重继承,而且我犯了一个错误,即没有考虑到即使使用单一继承,这种情况也是可能的:
class A
{
public:
unsigned int m_uiField;
};
class B : public A
{
virtual void VirtualMethod()
{
}
};
int main()
{
unsigned int uiOffsetA(reinterpret_cast<unsigned int>(&(reinterpret_cast<A *>(0)->m_uiField)));
// uiOffsetA is 0 on VC9
unsigned int uiOffsetB(reinterpret_cast<unsigned int>(&(reinterpret_cast<B *>(0)->m_uiField)));
// uiOffsetB is 4 on VC9
}
在这种情况下,我的编译器放在每个 B 实例开头的虚拟表指针将 A 的字段偏移 4 个字节。
我的第一个想法是做一些类似于我对字段偏移量所做的事情,并将单个无符号整数存储为基类的偏移量,以添加到指向派生类实例的指针以及字段偏移量。因此,在初始化时,我为从基类继承的每个派生类调用此函数:
template <typename Base, typename Derived>
unsigned int GetBaseClassOffset()
{
Derived *pDerived(reinterpret_cast<Derived *>(4));
Base *pBase(pDerived);
assert(pBase >= pDerived);
return reinterpret_cast<unsigned int>(pBase) - reinterpret_cast<unsigned int>(pDerived);
}
一切似乎都适用于我使用 VC9 进行的测试。 但后来我想到,C++ 的这个领域可能依赖于实现,而对齐等其他事情可能会打破这一点。
最后我的问题是: 我可以假设基类的字段总是相对于指向派生类实例的指针定位在一个恒定的正偏移量处吗?
注意:我并不是说“在所有编译器中都保持不变”,我将使用一些代码(最终取决于编译器)在启动时检测此偏移量。
【问题讨论】:
-
我很确定你不能假设任何这样的事情。继承的实现(尤其是多态、多重和虚拟继承)完全取决于编译器。我认为您可以假设的唯一事情是成员对象按其声明顺序排序,并且它们被适当地对齐。
-
@sehe: "仅限 POD 类型"。
-
Valerio,注意指针在 32 位操作系统上是 4 个字节。在 64 位上,指针是 8 个字节。
-
我见过很多用 C++ 进行反射的尝试,它们都是非常讨厌的 hack。只需在基于块的流(如.png)中使用
operator <<和operator >>,其中块头是通过虚拟GetClassType或其他东西访问的类类型。
标签: c++