使用多态基类(包含虚函数)访问时数组元素的类型

发布于 2024-12-22 13:11:23 字数 631 浏览 2 评论 0原文

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雨的味道风的声音 2024-12-29 13:11:23

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

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

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.

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.

可爱咩 2024-12-29 13:11:23

但是,这是错误的。有人可以解释一下为什么 p[0] 解析为
基地&而不是基地?是因为 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();

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()?

You got it -- it is the "array indirection operation".

The following are equivolent:

a[1];
*(a + 1)

It's in the standard: Literally, the "array indirection operation" means that *(a + n) substitutes for a[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:

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

[EDIT] To be explicit, in your example, yes, these are exactly equivalent:

p[0].foo();
(p+0)->foo();    // The "->" is the "*" indirection
(*(p+0)).foo();
公布 2024-12-29 13:11:23

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

如果您查看标准,就会发现这就是下标运算符的定义。

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

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

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

Is it because p[0].foo() equivalent to (p+0)->foo() ?

If you look into the standard, that's the very definition of the subscript operator.

The expression E1[E2] is identical (by definition) to *((E1)+(E2))

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() means p->foo(); the compiler will happily generate the code to call the method through the vptr, which has been initialized correctly before the Derived constructor to point to the Derived vtable. This all will result in Derived::foo() being called.

梦罢 2024-12-29 13:11:23

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文