基类字段偏移

时间:2011-09-18 19:58:36

标签: c++

我有一些代码实现了一种运行时反射。为了获得指向给定实例内的类的字段的指针,我基本上将指针指向类实例,并为每个暴露给反射库的字段添加一次计算的固定偏移量。

我保持实现非常简单,因为我不需要支持多重继承,我犯了一个错误,即没有考虑到,即使是单继承,这种情况也是可能的:

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个字节。

我的第一个想法是做类似于我正在为字段偏移做的事情,并将一个unsigned int存储为基类的偏移量,以添加到派生类实例的指针以及字段偏移量。因此,在初始化时,我为从Base类继承的每个Derived类调用此函数:

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 ++的这个领域可能依赖于实现,而像对齐这样的其他东西可能会破坏它。

最后我的问题是: 我可以假设基类的字段总是位于相对于派生类实例的指针的常量正偏移处吗?

注意:我并不是说“在所有编译器中保持不变”,我将使用一些代码(最终依赖于编译器)来启动时检测此偏移量。

3 个答案:

答案 0 :(得分:2)

对于这种情况,您可以使用指向成员的指针:

现场直播:http://ideone.com/U4w7j

struct A
{
    unsigned int m_uiField;
};

struct B : A
{
    virtual void VirtualMethod() { }
};

int main()
{
    A instance_a;
    B instance_b;

    unsigned int A::*  ptrA = &A::m_uiField;
    unsigned int B::*  ptrB = &B::m_uiField;

    // application:
    unsigned int value = instance_a.*ptrA;
                 value = instance_b.*ptrA;
               //value = instance_a.*ptrB; // incompatible types
                 value = instance_b.*ptrB;

    // also:
    A* dynamic = new B();
    value = dynamic->*ptrA; // etc
}

我建议你看看模板元编程功能(现在是TR1和C ++ 11的一部分:),特别是is_pod类型特性:

这很重要,因为在其他任何地方使用offsetof都是危险的。

答案 1 :(得分:1)

C ++(包括C ++ 11)中反射的符合标准的实现是不可能的。主要是因为成员偏移不是标准化的,所以取决于编译器。它取决于polimorphism实现,对齐以及可能还有其他事情。

您可以为特定编译器或有限范围的类实现反射。

Much more info about C++ reflection is here

为C ++ 11标准提出了反射支持,但由于需要更多时间而被推迟。

答案 2 :(得分:0)

  

我可以假设基类的字段总是位于相对于派生类实例的指针的常量正偏移处吗?

不,你不能。为什么这是一个可能的抵消?我希望在常见的实现中,基类成员放在之前派生类?

注意标准的'运算符'(不太完整)offsetof,它将为您提供从结构到成员的偏移量。它通常实现为类似于你的宏,引用空指针。它可能不会帮助你,因为它保证只适用于POD类型类。见标准的18.2 / 4:

  

宏offsetof(type,member-designator)在此International中接受一组受限制的类型参数   标准。如果type不是标准布局类(第9条),则结果未定义。