C++ 如何获得;直接;指向虚拟成员函数的函数指针?

C++ 如何获得;直接;指向虚拟成员函数的函数指针?,c++,pointers,C++,Pointers,我在一个嵌入式平台上工作,它不能很好地处理动态代码(根本没有推测性的/OOO执行)。 在这个平台上,我经常在同一个对象上调用虚拟成员函数,但是编译器无法优化vtable查找,因为它似乎没有意识到查找只在第一次调用时才需要 因此,我想知道:是否有一种手动方式来取消C++类的虚拟成员函数,以便得到一个函数指针,它直接指向解析的地址? 我看了C++函数指针,但是因为它们似乎需要指定的类型,所以我猜这不会起作用。 提前感谢如果只提供对基类对象的引用,则没有通用的标准C++唯一方法来查找虚拟函数的地址。此

我在一个嵌入式平台上工作,它不能很好地处理动态代码(根本没有推测性的/OOO执行)。 在这个平台上,我经常在同一个对象上调用虚拟成员函数,但是编译器无法优化vtable查找,因为它似乎没有意识到查找只在第一次调用时才需要

因此,我想知道:是否有一种手动方式来取消C++类的虚拟成员函数,以便得到一个函数指针,它直接指向解析的地址? 我看了C++函数指针,但是因为它们似乎需要指定的类型,所以我猜这不会起作用。
提前感谢

如果只提供对基类对象的引用,则没有通用的标准C++唯一方法来查找虚拟函数的地址。此外,没有合理的类型,因为
this
不需要按照一般约定作为普通参数传递(例如,它可以在寄存器中传递,其他参数在堆栈上)

但是,如果您不需要可移植性,您可以随时为给定的编译器执行任何工作。例如,微软的COM(我知道,那不是你的平台)有一个已知的内存布局,带有vtable指针,以便从C访问功能

如果您确实需要可移植性,那么我建议在优化中进行设计。例如,代替

class Foo_base
{
public:
    virtual void bar() = 0;
};
喜欢

class Foo_base
{
public:
    typedef (*Bar_func)(Foo_base&);

    virtual Bar_func bar_func() const = 0;
    void bar() { bar_func()( *this ); }
};

支持与以前相同的公共接口,但现在可以说是公开了内部,从而允许手动优化对
bar

的重复调用,关于gcc,我在调试编译的汇编代码时看到了以下内容。 我已经看到,通用方法指针包含两个数据: a) 指向该方法的“指针” b) 最终添加到类实例起始地址的偏移量(当涉及多个继承时,以及第二个和更多父类的方法时使用该偏移量,如果应用于它们的对象,则它们的数据将位于不同的起始点)

指向该方法的“指针”如下所示: 1) 如果“指针”为偶数,则将其解释为正常(非虚拟)函数指针。 2) 如果“指针”为奇数,则应减去1,剩余值应为0或4或8或12(假设指针大小为4字节)。 前面的编码显然假设所有正常方法都是从偶数地址开始的(因此编译器应该将它们对齐到偶数地址)。 因此,偏移量是vtable中的偏移量,在vtable中获取“真实”非虚拟方法指针的地址

因此,要使调用设备化,正确的想法是将虚拟方法指针转换为非虚拟方法指针,并在以后使用它,以便将其应用于类实例“subject”

下面的代码实现了所描述的功能

#include <stdio.h>
#include <string.h>
#include <typeinfo>
#include <typeindex>
#include <cstdint>

struct Animal{
    int weight=0x11111111;
    virtual int mm(){printf("Animal1 mm\n");return 0x77;};
    virtual int nn(){printf("Animal1 nn\n");return 0x99;};
};

struct Tiger:Animal{
    int weight=0x22222222,height=0x33333333; 
    virtual int mm(){printf("Tigerxx\n");return 0xCC;}
    virtual int nn(){printf("Tigerxx\n");return 0x99;};
};

typedef int (Animal::*methodPointerT)();

typedef struct {
    void** functionPtr;
    size_t offset;
} MP;

void devirtualize(methodPointerT& mp0,const Animal& a){
    MP& t=*(MP*)&mp0;
    if((intptr_t)t.functionPtr & 1){
        size_t index=(t.functionPtr-(void**)1); // there is obviously a more
        void** vTable=(void**)(*(void**)&a);        // efficient way. Just for clearness !
        t.functionPtr=(void**)vTable[index];
    }
};

int main()  
{
    int (Animal::*mp1)()=&Animal::nn;
    MP& mp1MP=*(MP*)&mp1;

  Animal x;Tiger y;

    (x.*mp1)();(y.*mp1)();

    devirtualize(mp1,x);

    (x.*mp1)();(y.*mp1)();

}
#包括
#包括
#包括
#包括
#包括
结构动物{
整数权重=0x11111111;
virtual int mm(){printf(“Animal1 mm\n”);返回0x77;};
虚拟int nn(){printf(“Animal1 nn\n”);返回0x99;};
};
结构虎:动物{
内部重量=0x2222,高度=0x33333333;
虚拟int mm(){printf(“Tigerxx\n”);返回0xCC;}
虚拟int nn(){printf(“Tigerxx\n”);返回0x99;};
};
typedef int(动物::*methodPointerT)();
类型定义结构{
无效**功能PTR;
尺寸偏差;
}议员;
无效设备化(methodPointerT和mp0、常量动物和a){
MP&t=*(MP*)和mp0;
如果((输入)t.functionPtr&1){
size_t index=(t.functionPtr-(void**)1);//显然有一个
void**vTable=(void**)(*(void**)和a);//有效的方法。只是为了清晰!
t、 functionPtr=(void**)vTable[索引];
}
};
int main()
{
int(动物::*mp1)(=&Animal::nn;
MP&mp1MP=*(MP*)和mp1;
动物x;老虎y;
(x.*mp1)(;(y.*mp1)();
设备化(mp1,x);
(x.*mp1)(;(y.*mp1)();
}

可能的重复感谢您指出这可能是重复的,但是另一个线程中的问题没有得到回答:/当您说“它似乎没有意识到查找只在第一次调用时才需要”时,您的意思是,在单个函数中,是否保证基类指针始终指向同一实例?(例如,VBase*b=新的子类();b->virtualfunc();…)。