【问题标题】:Type of an array element when accessed with polymorphic base class (containing virtual functions)使用多态基类(包含虚函数)访问时数组元素的类型
【发布时间】:2012-01-27 08:36:17
【问题描述】:

virtual 函数解析发生在指针/引用而不是对象上。现在考虑下面的例子:

struct Base { virtual void foo (); };
struct Derived : Base { void foo (); };

Derived d[2];
Base *p = d;
p[0].foo();  // calls Derived::foo()!

我的看法是这样的:对于任何数组T arr[SIZE];arr[N] 的类型是T(而不是T&),即arr[N] 是一个对象。如果是这种情况,那么在上面的示例中,p[0] 将调用Base::foo(),因为p[0] 应该解析为一个对象。

但是,这是错误的。有人可以解释一下,为什么 p[0] 解析为 Base&not Base ?是不是因为p[0].foo()等价于(p+0)->foo()

【问题讨论】:

  • 这正是虚函数被发明的原因——所以指向基础对象的指针可以调用派生函数。我不知道你为什么这么惊讶。

标签: c++ arrays object reference virtual-functions


【解决方案1】:

但是,这是错误的。有人可以解释一下,为什么 p[0] 正在解决 Base& 而不是 Base ?是不是因为 p[0].foo() 相当于 (p+0)->foo()?

你明白了——它是“数组间接操作”。

以下是等价的:

a[1];
*(a + 1)

在标准中:从字面上看,“数组间接操作”意味着*(a + n) 替换了a[n],因此结果是对对象的引用,而不是对象的复制构造值。

这对于使多态性发挥作用以及确保派生类型不会“复制构造”到基本类型至关重要。

进一步扩展,以下也是等价的,并导致对对象的引用:

a[1][2][3];
*(*(*(a + 1) + 2) + 3);

[编辑] 明确地说,在你的例子中,是的,这些是完全等价的:

p[0].foo();
(p+0)->foo();    // The "->" is the "*" indirection
(*(p+0)).foo();

【讨论】:

    【解决方案2】:

    它必须解析为引用,因为可以使用下标表示法修改数组元素。

    解析到一个对象意味着结果是一个副本,并且改变它要么是不可能的,要么会使原始数组保持不变。

    【讨论】:

      【解决方案3】:

      是因为p[0].foo() 等同于(p+0)->foo() 吗?

      如果您查看标准,那就是下标运算符的定义。

      表达式E1[E2]*((E1)+(E2)) 相同(根据定义)

      并且取消引用的结果是“引用对象的左值”。只有在切片发生时(即运行基类型的复制构造函数以创建原始对象的副本),您才会丢失动态类型信息,而这里绝对不会。

      至于“实际生成的代码”,我看不出问题出在哪里……p[0].foo()表示p->foo();编译器将愉快地生成代码以通过 vptr 调用该方法,该 vptr 在 Derived 构造函数之前已正确初始化以指向 Derived vtable。这一切都会导致Derived::foo() 被调用。

      【讨论】:

        【解决方案4】:

        我的看法是这样的:对于任何数组 T arr[SIZE];的类型 arr[N] 是 T(而不是 T&),即 arr[N] 是一个对象。如果它是一个 情况下,那么在上面的示例中 p[0] 会调用 Base::foo(),因为 p[0] 应该解析为一个对象。

        请注意,编译器将假定所有元素都具有sizeof(T) 大小。这意味着访问除第一个元素之外的任何元素都是未定义的行为。

        【讨论】:

        • 这很有趣。我从来没有注意到这一点,现在我明白了。 +1
        • 一般来说,这点是非常正确的,但是在上面的例子中,派生类型的大小没有改变,所以代码可以工作。此外,仅通过更改方法而不添加数据来扩展类并不罕见。不过,在某种更微妙的意义上,这可能是未定义的行为。
        • @Adrian:“我添加了一个字段,现在我的代码已损坏”的感觉。那不是真正有效的代码。
        • 哦,它工作正常。很多破碎的东西工作。一会儿。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-20
        • 2013-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多