在编译时计算基类的偏移量
Calculate offset of base class at compile time
我想知道在编译时是否可以计算基类偏移量。当然,这在运行时相当容易,因为可以利用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。有没有另一种方法可以在编译时实现我的目标,或者我最好在运行时计算 ofset 并有较小的运行时开销?
编辑:
一个有效的运行时实现可能如下所示:
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
}
这种方法会产生准确的结果,但代价是运行时开销很小(如果无法在编译时计算偏移量以消除此开销,则运行时开销是可以接受的)。
编译器可以在基类IInterface1
和IInterface2
之间的CClas
中引入填充。
基本上X: sizeof(CClas)>= sizeof(IInterface1) + sizeof(IInterface2)
然后,以下语句可能会屈服于错误的地址:
面2* pI2 = reinterpret_cast<IInterface2*>(reinterpret_cast<char*>(&instance) + 偏移I2
X请注意,如果基类没有virtual
成员函数并且由于空基优化而为空(即没有数据成员),则这可能不成立。
相关文章:
- std::具有相同基类的类的变体
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 基类中的函数名称解析
- boost::序列化中的派生类偏移量计算.有效吗?
- 使用基地址和偏移量获取变量的地址
- 变量(继承自基模板类)的偏移量,由具有相同名称的成员遮蔽
- 在编译时计算基类的偏移量
- 是否可以在 c++ 中的派生类中以特定偏移量附加结构
- 获取多重继承中基类的编译时常量偏移量
- 对相同基类的引用在内存中必须具有单独的偏移量
- 对模板类使用偏移量
- 偏移量中的钩子函数类(从.dll中的.exe)
- 编译时基类指针偏移到派生类
- C++通过指针和偏移量从类中读取
- standard_layout类的数据成员是否与对象的地址有固定的偏移量?
- 虚函数表中用于虚继承的虚基偏移量
- 抽象基类的标量,矢量,张量,
- 基类字段偏移量
- 是否可以手动计算类成员的字节偏移量