动态类型的含义
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 而不是对象的 指针在 继承层次结构 中来回切换.
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 而不是对象的 指针在 继承层次结构 中来回切换.