咨询:派生类转空基类指针地址不变的标准依据及代码现象解释
C++派生类向上转换为基类的偏移量行为解释及标准依据
示例代码
#include <cstdio> struct base { int value; }; template <typename T> struct wrapper: base, T { }; struct empty {}; struct nonempty { int value; }; template <typename T> int ofst(wrapper<T> *t) { return (char *) ((T *) t) - (char *) t; } template <typename T> int ofst2(wrapper<T> *t) { return (char *) static_cast<T*>(t) - (char *) t; } int main() { wrapper<empty> empty; wrapper<nonempty> nonempty; printf("%d %d\n", ofst(&empty), ofst(&nonempty)); // 输出 0, 4 printf("%d %d\n", ofst2(&empty), ofst2(&nonempty)); // 同样输出 0, 4 }
结果解释
wrapper<empty>偏移量为0:empty是空类,编译器会启用空基类优化(EBO)——不为空基类子对象分配独立存储空间,允许其地址与派生类对象起始地址重叠。因此将wrapper<empty>*转换为empty*时,偏移量为0。wrapper<nonempty>偏移量为4:nonempty是非空类,包含一个4字节的int成员。wrapper的基类布局中,base子对象(4字节)排在前面,nonempty子对象紧随其后,所以转换时需要偏移4字节才能指向nonempty子对象的起始地址。
C++标准参考依据
- 空基类优化规则:
C标准(如C20 [class.mem]/19)规定,当空类作为基类,且该基类子对象不是最派生对象、未被其他非静态成员或基类子对象的布局覆盖时,编译器可以不为其分配存储空间,允许地址重叠。 - 基类指针转换规则:
根据[expr.static.cast]/13和[expr.cast]/4,将派生类指针转换为基类指针时,结果必须指向对应基类子对象的起始地址,偏移量由类的布局决定。非空基类的偏移量是派生对象起始地址到基类子对象起始地址的字节数;空基类若启用EBO,偏移量为0。 - 指针算术有效性:
[expr.add]/6允许将指针转换为char*后进行减法运算,只要两个指针指向同一对象的不同子对象,结果即为地址间的字节差,符合代码中计算偏移量的逻辑。
内容的提问来源于stack exchange,提问作者Yuxai
相关产品推荐
相关产品推荐

