You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多态对象数组迭代:基类指针遍历派生类数组的标准偏移规则问询

多态类型数组通过基类指针遍历的C++标准问题

请考虑以下代码:

#include <iostream>

struct B {
    char i;
    B(char i) : i(i) {};
    void bar() {};
};

struct D : B {
    int y;
    D(char i, int y) : B(i), y(y) {};
};

void foo(B *arr, size_t size)
{
    for(B *end = arr + size; arr < end; ++arr) {
        std::cout << arr->i << std::endl;
    }
}

int main()
{
    D arr[3] = { {'a', 65}, {'b', 66}, {'c', 67} };
    foo(arr, sizeof(arr) / sizeof(*arr));
}

正如预期,这段代码仅输出a——准确来说是a以及基类B中i之后的两个填充字节。

接着,若将B的成员函数bar改为虚函数,会使两个类都成为多态类型。在此配置下,clang和gcc都会输出abc,看起来像是基于多态类型和某些运行时信息计算偏移量,但这显然不合理。

我还尝试添加另一个布局不同的派生类:

struct C : B {
    long y;
    C(char i, long y) : B(i), y(y) {};
};
//...
C rra[] = { {'a', 65}, {'b', 66}, {'c', 67} };
foo(rra, sizeof(rra) / sizeof(*rra));

测试中输出了奇怪的apB,和初始化值不符,这说明并没有使用运行时信息计算偏移量。

因此我的问题非常明确:

  • 在多态场景下,通过基类指针遍历派生类对象数组时,C++标准规定应使用哪种偏移量?

我查阅了C++标准,未发现任何关于运行时信息影响偏移量的描述。[expr.add]条款并未明确这一点,仅指出结果指针必须指向数组的某个元素。

当修改foo函数的输出逻辑后,情况变得更奇怪:

#include <iostream>

struct B {
    char i;
    B(char i) : i(i) {};
    virtual void foo() { std::cout << "I AM BASE" << i << std::endl; };
};

struct D : B {
    int y;
    D(char i, int y) : B(i), y(y) {};
    virtual void foo() { std::cout << "I AM DERIVED" << i << std::endl; };
};

struct C : B {
    long y;
    C(char i, long y) : B(i), y(y) {};
    virtual void foo() { std::cout << "I AM CERIVED" << i << std::endl; };

};

void foo(B *arr, size_t size)
{
    for(B *end = arr + size; arr < end; ++arr) {
        std::cout << arr->i << std::endl;
        arr->foo();
    }
}

int main()
{
    D arr[] = { {'a', 65}, {'d', 66}, {'c', 67} };
    foo(arr, sizeof(arr) / sizeof(*arr));
    C rra[] = { {'a', 70}, {'d', 66}, {'c', 67} };
    foo(rra, sizeof(rra) / sizeof(*rra));
}

测试中,第一次迭代会正确输出I AM DERIVED及对应的字符,而第二次仅输出一个CERIVED后,程序因SIGSEGV崩溃。该问题可在最新版本的clang和gcc中复现。

编辑补充
技术层面上问题已得到解释。若好奇为何带虚函数时,通过基类指针遍历D对象数组能正常工作(这是我最初的疑问),我的推测是:虚函数会为结构体添加虚表指针,迫使结构体按8字节边界对齐。这使得B和D的大小变为16字节,C变为24字节(基于示例使用的平台)。因此遍历D数组能正常工作只是巧合——类大小相同,但这仍然是未定义行为(UB)。


内容的提问来源于stack exchange,提问作者tntnkn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 09:35:05