使用多态基类(包含虚函数)访问时数组元素的类型
虚拟函数解析发生在指针/引用上,而不是对象上。现在考虑下面的例子:
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]
< em>应该解析为一个对象。
然而,这是错误的。有人可以解释一下,为什么 p[0]
解析为 Base&
而不是 Base
?是因为p[0].foo()
相当于(p+0)->foo()
吗?
virtual
function resolution happens with pointer/reference and not with object. Now consider below example:
struct Base { virtual void foo (); };
struct Derived : Base { void foo (); };
Derived d[2];
Base *p = d;
p[0].foo(); // calls Derived::foo()!
My perception was like this: for any array T arr[SIZE];
the type of arr[N]
is T
(and not T&
), i.e. arr[N]
is an object. Had it been a case, then in above sample p[0]
would call Base::foo()
, because p[0]
should resolved to an object.
However, it's wrong. Can someone explain, why p[0]
is resolving to Base&
and not Base
? Is it because p[0].foo()
is equivalent to (p+0)->foo()
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
请注意,编译器将假设所有元素都具有
sizeof(T)
大小。这意味着访问除第一个元素之外的任何元素都将是未定义行为。Be aware that the compiler will assume that all elements have
sizeof(T)
size. This means that accessing any element except the first will be Undefined Behaviour.你明白了——这就是“数组间接操作”。
以下是等效的:
在标准中:从字面上看,“数组间接操作”意味着
*(a + n)
替换a[n]
,因此结果是对对象的引用,而不是对象的复制构造值。这对于使多态性发挥作用并确保派生类型不会“复制构造”为基类型至关重要。
进一步扩展,以下内容也是等价的,并导致对对象的引用:
[编辑] 明确地说,在您的示例中,是的,这些是完全等价的:
You got it -- it is the "array indirection operation".
The following are equivolent:
It's in the standard: Literally, the "array indirection operation" means that
*(a + n)
substitutes fora[n]
, so the result is a reference to an object, not a copy-constructed value of an object.This was essential to get polymorphism to work, and to ensure derive types were not "copy-constructed" to base types.
Extending further, the following are also equivolent, and result in a reference-to-object:
[EDIT] To be explicit, in your example, yes, these are exactly equivalent:
如果您查看标准,就会发现这就是下标运算符的定义。
,并且取消引用的结果是“引用目的”。仅当发生切片时(即,当运行基本类型的复制构造函数来创建原始对象的副本时),您才会丢失动态类型信息,而这里绝对不会。
至于“实际生成的代码”,我不明白问题出在哪里......
p[0].foo()
意味着p->foo()
代码>;编译器会愉快地生成通过 vptr 调用方法的代码,该 vptr 在Derived
构造函数之前已正确初始化以指向Derived
vtable。这一切都将导致Derived::foo()
被调用。If you look into the standard, that's the very definition of the subscript operator.
and the result of the dereferentiation is "an lvalue referring to the object". You lose the dynamic type information only when slicing happens (i.e. when the copy constructor of the base type is run to create a copy of your original object), and here it definitely doesn't.
As for the "actual generated code" is concerned, I don't see where's the problem...
p[0].foo()
meansp->foo()
; the compiler will happily generate the code to call the method through the vptr, which has been initialized correctly before theDerived
constructor to point to theDerived
vtable. This all will result inDerived::foo()
being called.它必须解析为引用,因为可以使用下标表示法修改数组元素。
解析为对象意味着结果是一个副本,并且更改它要么是不可能的,要么会使原始数组保持不变。
It has to resolve to a reference because array elements can be modified using subscript notation.
Resolving to an object would imply that the result was a copy, and that changing it was either impossible, or would leave the original array unchanged.