C 海关;“非传统”;多态性实现

C 海关;“非传统”;多态性实现,c,polymorphism,vtable,binary-compatibility,C,Polymorphism,Vtable,Binary Compatibility,我一直在寻找一个定制的多态解决方案来提高二进制兼容性。问题是指针成员在不同的平台上大小不一,所以即使是“静态”宽度成员也会被推来推去,产生二进制不兼容的布局 据我所知,大多数编译器以类似的方式实现v表: __________________ | | | | |0| vtable* | data | -> -> |_|_________|______| 例如,v-table被放置为对象的第一个元素,在多重继承的情况下,继承的类按顺

我一直在寻找一个定制的多态解决方案来提高二进制兼容性。问题是指针成员在不同的平台上大小不一,所以即使是“静态”宽度成员也会被推来推去,产生二进制不兼容的布局

据我所知,大多数编译器以类似的方式实现v表:

    __________________
   | |         |      |
   |0| vtable* | data | -> -> 
   |_|_________|______|
例如,v-table被放置为对象的第一个元素,在多重继承的情况下,继承的类按顺序与适当的填充对齐

因此,我的想法如下,将所有v形桌(以及所有变化的“平台宽度”成员)“放在后面”:

在使用6000万个对象(7000万个对象在我当前使用的32位编译器上分配失败)进行测试后,调用常规虚拟函数和通过不是对象中第一个元素的指针调用(因此需要额外的偏移量计算)之间似乎没有任何可测量的区别,即使在函数指针的情况下,内存地址也会被传递两次,例如,传递以查找
a
的偏移量,然后传递到
a
)。在发布模式下,两个函数的时间是相同的(+/-1nsec用于60mil调用),而在调试模式下,函数指针实际上一致地快了约1%(可能函数指针需要的资源比虚拟函数少)


在分配和删除指针时调整指针的开销几乎可以忽略,并且完全在误差范围内。这在某种程度上是意料之中的,考虑到它应该只增加寄存器中已存在的值的一个增量和一个立即值,这在我打算瞄准的平台上应该需要一个周期。

FYI,vtable的地址放在对象的第一个DWORD/QWORD,而不是表本身。vtable在同一类/结构的对象之间共享

顺便说一句,平台之间具有不同的vtable大小不是问题。不兼容的平台无法执行其他平台的本机代码,为了使二进制转换正常工作,仿真器需要了解原始架构


您的解决方案的主要缺点是当前实现的性能和复杂性。

为什么函数指针在不同的平台上可能大小不同?你不是在平台之间传递指针,是吗?你是在描述一个需要帮助的解决方案,但不要详细说明它应该解决的问题(所谓的问题)。您还讨论了多态性和
这个
指针,同时将问题标记为
C
,而该语言没有类似的问题。你可能在制作自己的C++或java(或类似)编译器吗?什么是二进制兼容所需的“数据”?为什么你需要它是二进制兼容的(这本身可能是一项巨大的任务)?@JoachimPileborg-我只是使用熟悉的习惯用法,我绝不意味着C语言中有
这个
新的
删除
,而它被标记为
C
的原因是我实际上是用C实现的是用户数据(除了指针和pointerdiff之外的所有数据)-实际上包含用户数据的成员,默认情况下,这些数据将显式调整大小并对齐(无不明确的
int
)。我在实现上不需要太多帮助,但有经验的人可能会提前发现这种方法的弱点。在平台之间传递数据时,有没有理由不使用标准序列化技术?推送原始结构内容是自找麻烦。如果指向成员的指针大小不同,那么使用相同的布局(比如vtable)有什么意义呢?我从来没有说过问题在于v-table的大小或布局,问题在于对象的大小和布局,以及不同指针宽度的平台之间的差异。您所设想的性能缺陷是什么?每次虚拟函数调用都会受到几个周期的惩罚,因为编译器将添加代码来偏移对象的地址以查找vtable地址。只有在分配对象时才进行调整,当您调用虚拟函数时,它将直接使用调整后的地址。我怀疑实际的调整不会比计算访问任何常规成员的偏移量慢,例如,为特定类型的立即数添加“基址”地址的单个时钟。
       __________________
      |         | |      |
   <- | vtable* |0| data | ->
      |_________|_|______|
class A;
typedef void (*foo)(A*);

void bar(A*) {}

class A {
public:
    A() : a(&bar) { }
    foo a;
    virtual void b() {}
};

int main() {
    QVector<A*> c;
    int r = 60000000;

    QElapsedTimer t;
    for (int i = 0; i < r; ++i) c.append(new A);
    cout << "allocated " << c.size() << " object in " << (quint64)t.elapsed() << endl;

    for (int count = 0; count < 5; ++count) {
        t.restart();
        for (int i = 0; i < r; ++i) {
            A * ap = c[i]; // note that c[i]->a(c[i]) would 
            ap->a(ap);     // actually result in a performance hit
        }
        cout << t.elapsed() << endl;

        t.restart();
        for (int i = 0; i < r; ++i) {
            c[i]->b();
        }
        cout << t.elapsed() << endl;
    }
}