vtable 的编译时检测

Compile-time detection of vtable

本文关键字:检测 编译 vtable      更新时间:2023-10-16

有没有办法在编译时检测一个类是否有vtable?我正在努力确保类与 64 字节边界对齐,长度为 64 字节。添加 vtable 会增加类大小为 128 字节。

class __attribute__((aligned(64))) C
{
private:
    int64_t iValue;
    char iPadding[64 - sizeof(int64_t)];
};

这很好。然而

class __attribute__((aligned(64))) C
{
public:
    virtual ~C() {}
private:
    int64_t iValue;
    char iPadding[64 - sizeof(int64_t)];
};

把事情搞砸了。

答:aligned也垫,而不仅仅是控制位置。 __declspec(align())似乎也在做同样的事情!

编辑:仍然被迷惑。在 C 的构造函数中检查this是否可以被 64 整除并throw异常(如果不是)后,我得到了异常。最初,我认为这可能与堆栈上的C实例有关,但是将它们更改为基于堆后,对齐检查仍然失败。我将回退到调用posix_memalign并执行就地new的工厂函数(这可能是std::aligned_storage最终所做的)

与其手动添加填充字节,为什么不直接使用__attribute__((aligned(64)))并让编译器为您对齐它,无论是否存在 vtable?然后,您将始终获得 64 字节对齐,无需其他工作,并且它消除了对了解 vtable 大小的依赖。

是的,你可以;使用 std::is_polymorphic .

如果您尝试对齐某些内容,请使用带有放置newstd::aligned_storage

std::aligned_storage<sizeof(C), 64> as;
C* c = new (&as) C;
// use c...
c->~C(); // call destructor ourselves

停止尝试手动添加填充字节。 只需定义要与对齐属性对齐的变量,如下所示:

class C
{
public:
    virtual ~C() {}
//private:
    __attribute__((aligned(64))) int64_t iValue;
};
void printAddr(C* a, int i)
{
    printf("&a[%d] = %p   &a[%d].iValue = %pn", i, &a[i], i, &a[i].iValue);
}
int main()
{
    C a[8];
    printf("nsizof(C) is: %dnn", sizeof(C));
    for (int i=0; i<sizeof(a)/sizeof(a[0]); ++i)
        printAddr(a, i);
    printf("n");
}
如果你的类

有一个 vtable,这当然会扩大类的大小,但编译器会为你插入必要的填充,并根据需要调整包含类的对齐要求,以确保具有最严格(最大)对齐的成员将正确对齐。 顺便说一句,vtable 不存储在对象中,只存储在 vtable 指针中。 因此,如果您使用的是 32 位系统,则 vtable 指针仅占用 4 个字节。 您看到的 sizeof(C)=128 的其余部分是编译器添加的填充,以确保 C 类型的对象数组对齐,但它正在将您关心的变量移出对齐:假设您实际上需要在 64 字节边界上对齐iValue, 它不再在这样的边界上对齐。


或。。。。 如果您实际上不必在 64 字节边界上对齐iValue本身,并且只需要在 64 字节边界上对齐类,但又不想不必要地向类添加体积,那么答案更简单:

停止添加自己的填充字节! 只需这样做:

class __attribute__((aligned(64))) C
{
public:
    virtual ~C() {}
//private:
    int64_t iValue;
};
void printAddr(C* a, int i)
{
    printf("&a[%d] = %p   &a[%d].iValue = %pn", i, &a[i], i, &a[i].iValue);
}
int main()
{
    C a[8];
    printf("nsizof(C) is: %dnn", sizeof(C));
    for (int i=0; i<sizeof(a)/sizeof(a[0]); ++i)
        printAddr(a, i);
    printf("n");
}

请注意,如果这样做,iValue成员不再在 64 字节边界上对齐,但它们都与相邻成员的iValue相距 64 个字节。