迭代多态对象数组

问题描述 投票:0回答:1

考虑以下代码:

#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
以及基类
i
B
后面的两个填充字节。

然后想象一下,我们将

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
,这不是我初始化的结果。所以似乎没有使用运行时信息来计算偏移量。

所以,我的问题非常简单:

  1. 当通过指向多态上下文中的基数的指针迭代派生数组时,根据标准将使用哪个偏移量?

我筛选了标准,发现没有提到影响偏移量的运行时信息。

[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
打印
char
,在第二次迭代中仅打印一个
CERIVED
,然后失败并显示
SIGSEGV

我可以用最新的和 gcc 来重现它。 godbolt 的链接

c++ arrays pointers pointer-arithmetic
1个回答
0
投票

您的

foo()
循环表现出未定义的行为

它需要一个指向

B[]
数组(第一个元素)的指针,并且只能迭代
B
对象。 但是,您将传入指向
C[]
D[]
数组的指针,其中
sizeof(C) > sizeof(B)
sizeof(D) > sizeof(B)
,因此迭代使用的指针算术将关闭。 指针算术仅按指针本身类型的大小前进,而不是所指向的对象的大小。

要执行您正在尝试的操作,您必须使用

virtual
方法,并且必须传入指向
B*
指针数组的指针,而不是指向对象数组的指针,例如:

#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) {};
    void foo() override { std::cout << "I AM DERIVED: " << i << std::endl; };
};

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

void foo(B* arr[], size_t size)
{
    for(size_t i = 0; i < size; ++i) {
        arr[i]->foo();
    }
}

int main()
{
    D arr[] = { {'a', 65}, {'d', 66}, {'c', 67} };
    B* arr_d[] = { &arr[0], &arr[1], &arr[2] };
    foo(arr_d, sizeof(arr_d) / sizeof(*arr_d));

    C rra[] = { {'a', 70}, {'d', 66}, {'c', 67} };
    B* arr_c[] = { &rra[0], &rra[1], &rra[2] };
    foo(arr_c, sizeof(arr_c) / sizeof(*arr_c));
}

在线演示

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.