动态类型的含义

Meaning of dynamic type

C++ 标准说“...虚函数调用的解释取决于调用它的对象的类型(动态类型)”(第 252 页)然后

"if a pointer p whose static type is 'pointer to B' is pointing to an object of class D, derived from B, the dynamic type of *p is D"(第 2 页)。这里 B 是基础 class 和 D 派生的 class.

这似乎暗示(对我来说)如果我说

D d; 
B *p = new B(); 
*p = d; 

那么,如果f()B中是虚的,p->f()应该调用D::f(),这是错误的。我想我不清楚 "pointing to an object of class ..." 的含义。我知道,如果我说 p = &d,则会调用 D::f(),但我想知道为什么上面的错误。

D d; 
B *p = new B(); 
*p = d;

最后一行将 d 分配给 *p。这意味着它将使用 B 赋值运算符复制 D 实例,对象将是 sliced。在这种情况下 *p 的动态类型是 still B.

p = &d;

将指向 D 对象的指针分配给 p。在这种情况下不会发生切片,因为您只是在分配指针,而不是对象本身。本例中 *p 的动态类型是 D.

答案的症结在于,在 C++ 中,对象的动态类型 永远不会 改变。您正在考虑赋值表达式 *p = d 以某种方式将 *p 处的对象完全替换为对象 d。但事实并非如此。在 C++ 中,一个对象永远无法真正替代另一个对象。

由于涉及class类型,*p = d只调用classB的赋值运算符(即*p的静态类型)参数 D。 C++中的一个对象只能被操作,不能真的是"replaced."

当然,我们谈论复制对象、分配给它们等。但这只是为了方便,因为大多数时候,非常精确的语义并不重要,并且将 = 视为分配一个对象另一个很简单。但在深处,它要么是对目标对象的函数调用(对于 class 类型),要么是将某些位的值复制到目标对象的 space 中(对于原始类型)。目标对象本身始终保持不变。

正如上面 TartanLlama 所说,在第三行中,您 "wrongfully" 将 d 分配给 P 指向的对象,从而发生我们所说的切片。来自 专业 C++

...When upcasting, use a pointer or reference to the superclass to avoid slicing

D d; 
B *p = new B(); 
*p = d;

这意味着正确代码计算为下面的代码

D d;
B *p = new B();
p = &d;

D d;
B *p = new B();
p = static_cast<B*>(&d);

因此p(*p)指向的对象的动态类型还是D。这允许您通过向上转换或向下转换指向子 class 而不是对象的 指针在 继承层次结构 中来回切换.