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