派生对象数组到基础对象数组

Array of derived objects to array of base objects

本文关键字:对象 数组 派生      更新时间:2023-10-16

有多个问题与此问题非常相似,不同之处在于存在sizeof(Base) != sizeof(Derived)。由于明显的原因,这将不起作用(指针上应用的下标运算符是相对于指针大小的,而不是实际的反遍历类型)。然而,我想知道这个代码是否正确:

struct Base
{
    int Data;
};
struct Derived : public Base
{
};
int main()
{
    static_assert(sizeof(Base) == sizeof(Derived), "Sizes are not equal");
    Derived Data[10];
    Base* Ptr = Data;
    Ptr[3].Data = 5;
}

显然,由于大小相等,Ptr[3]将不再访问任何半撕裂的Base实例,但代码仍然正确吗?

是的,这是正确的(在定义明确的意义上,不一定是理智的),因为这两个类是布局兼容的-它们是具有相同非静态数据成员的标准布局结构。

不过它非常脆弱;对类的微小更改可能会破坏兼容性并产生未定义的行为。

假设我们有一个函数接收Derived

double foo(Derived d);

现在考虑一下代码的一个细微变化:

Derived Data[10];
Base* Ptr = Data;
Base myB;
Ptr[3] = myB;

本质上,我们已经将一个Base对象放入Data数组中。然后我们打电话给

foo(Data[3]);

瞧,我们诱骗foo接收Base

这就是不应将Derived的数组视为Base的数组的原因。这不仅仅是尺寸的问题。

让我们考虑以下行:

Derived Data[10];
Base* Ptr = Data;

CCD_ 9保存数组的第一个元素的地址。因此,它是一个类型为Derived*的指针。存在从Derived*Base*的静态强制转换。这意味着代码是正确的。

这是正确的,但通常不会这样做,因为当派生类的指针指向基对象时,数据可能会损坏。