【发布时间】:2017-05-15 07:01:23
【问题描述】:
我想知道在编译时是否可以计算基类偏移量。当然,在运行时这很容易做到,因为可以利用static_cast 的功能,并且偏移量只是基指针与派生类指针之间的差异。
我在编译时第一次尝试得到这个看起来像下面这样:
struct InterfaceRoot {};
struct IInterface1 : InterfaceRoot {
virtual void MethodI1() = 0;
};
struct IInterface2 : InterfaceRoot {
virtual void MethodI2() = 0;
};
class CClass : public IInterface1, public IInterface2 {
virtual void MethodI1() override { /* do something */ }
virtual void MethodI2() override { /* do something */ }
};
int main() {
CClass instance;
constexpr int offsetI1 = 0; //first base class has no offset
constexpr int offsetI2 = sizeof(IInterface1);
//check pointer values against static_cast
IInterface1* pI1 = reinterpret_cast<IInterface1*>(reinterpret_cast<char*>(&instance) + offsetI1);
IInterface2* pI2 = reinterpret_cast<IInterface2*>(reinterpret_cast<char*>(&instance) + offsetI2);
IInterface1* pI1_static_cast = static_cast<IInterface1*>(&instance);
IInterface2* pI2_static_cast = static_cast<IInterface2*>(&instance);
return 0;
}
在这里,pI1 和 pI1_static_cast 与预期的一样。但是,pI2 和 pI2_static_cast 不相等!?
我可以通过向InterfaceRoot 添加一个虚函数或将其全部删除来解决此问题。这是什么原因?
如果我像这样设置继承树,它可以使用上述方法:
struct InterfaceRoot {
virtual ~InterfaceRoot() {}
};
struct IInterface1 : InterfaceRoot {
virtual void MethodI1() = 0;
};
struct IInterface2 : InterfaceRoot {
virtual void MethodI2() = 0;
};
class CClass : public IInterface1, public IInterface2 {
virtual void MethodI1() override { /* do something */ }
virtual void MethodI2() override { /* do something */ }
};
有人知道这是为什么吗?顺便说一句,我正在使用 Visual Studio 2017。有没有其他方法可以在编译时实现我的目标,或者我最好在运行时计算偏移量并且运行时开销很小?
编辑:
一个有效的运行时实现可能如下所示:
template<typename Derived, typename Base>
inline int CalcBaseOffset() {
const static int s_off = (reinterpret_cast<char*>(static_cast<Base*>(reinterpret_cast<Derived*>(0x10000000))) - reinterpret_cast<char*>(0x10000000));
return s_off;
};
int main() {
//...
int offsetI1_RT = CalcBaseOffset<CClass, IInterface1>();
int offsetI2_RT = CalcBaseOffset<CClass, IInterface2>();
// add offsets to pointer like in the code sample above
}
这种方法会产生准确的结果,但代价是运行时开销很小(如果无法在编译时计算偏移量以消除这种开销,则运行时开销是可以接受的)。
【问题讨论】:
-
只是出于好奇,能否请您将
main()中所有四个指针的实际十六进制值提供给我们? -
@Mike Nakis
pI1 = 0x0077fe78,pI1_static_cast = 0x0077fe78,pI2 = 0x0077fe7c,pI2_static_cast = 0x0077fe80 -
请问您为什么要这样做?我发现很难找到一个用例。
-
我想实现一个类似COM的接口结构,我希望函数
QueryInterface(dynamic_cast的交叉编译器版本)由模板类自动生成。但是为了将this指针转换为基类指针,我需要偏移量。问题是,转换为指针值在编译时不起作用,所以我必须寻找其他可能性或在运行时实现算法,每次调用QueryInterface时都会产生运行时开销。