多种继承和多态性问题
Multiple inheritance and polymorphism questions
考虑此C 代码:
#include <iostream>
using namespace std;
struct B {
virtual int f() { return 1; }
int g() { return 2; }
};
struct D1 : public B { // (*)
int g() { return 3; }
};
struct D2 : public B { // (*)
virtual int f() { return 4; }
};
struct M : public D1, public D2 {
int g() { return 5; }
};
int main() {
M m;
D1* d1 = &m;
cout << d1->f()
<< static_cast<D2&>(m).g()
<< static_cast<B*>(d1)->g()
<< m.g();
}
它打印1225
。如果我们制作虚拟继承,即在标有(*)的行中添加virtual
,则它打印4225
。
- 您能解释为什么
1
更改为4
? - 您能解释
static_cast<D2&>(m)
和static_cast<B*>(d1)
的含义吗? - 您如何没有在这种组合中迷失?你在画什么吗?
- 在普通项目中发现这样的复杂设置是否常见?
图片胜于雄辩,所以在答案之前...
类m层次结构没有 b的虚拟基础继承D1和D2:
M
/
D1 D2
| |
B B
类M层次结构 b的虚拟基础继承D1和D2:
M
/
D1 D2
/
B
跨质量,或者我喜欢称之为兄弟姐妹 - 伴形曲折。虚拟碱基的继承将修复B :: f()覆盖为d2:f()。希望当您考虑虚拟函数的实现及其由于继承链的结果时,图片有助于解释这一点。
。在这种情况下,
static_cast
操作员用法可从派生到基础类型类型进行转换。很多经验阅读了真正糟糕的代码,并知道语言"工作"的基础
谢天谢地不。这不是常见的。不过,原来的iostream库会给您噩梦,如果这让所有人令人困惑。
您能解释为什么1个更改为4?
为什么会更改为4
?由于交叉污染。
这是虚拟继承之前的继承图:
B B
| |
D1 D2
/
M
d1
是D1
,因此甚至不知道D2
甚至存在,其父(B
)都不知道D2
存在。唯一可能的结果是B::f()
称为。
添加了虚拟继承后,基本类被合并在一起。
B
/
D1 D2
/
M
在这里,当您向d1
询问f()
时,它会向其父母看。现在,它们共享相同的B
,因此B
的f()
将被D2::f()
覆盖,您将获得4
。
是的,这很奇怪,因为这意味着D1
已设法从D2
调用功能,这对此一无所知。这是C 最奇怪的部分之一,通常可以避免。
您可以解释static_cast(m)和static_cast(d1)的含义吗?
您不了解什么?他们分别将m
和d1
分别投入到D2&
和B*
。
您如何没有在这种组合中迷失?你在画什么吗?
在这种情况下不是。它很复杂,但足够小,可以保持脑海。我在上面的示例中绘制了图表,以使事情尽可能清晰。
在普通项目中发现这样的复杂设置是否常见?
否。每个人都知道避免可怕的钻石继承模式,因为它太复杂了,通常有一种更简单的方法可以做任何您想做的事情。
通常,最好更喜欢构图而不是继承。
(1)您能解释为什么1个更改为4?
没有public
0继承,有2个独立的继承层次结构;B->D1->M
和B->D2->M
。因此,想象一下2个virtual
功能表(尽管已定义了实现)。
当您使用D1*
调用f()
时,IT Just 知道B::f()
,仅此而已。使用virtual
继承,将基础class B
委派给M
,因此D2::f()
被视为class M
的一部分。
(2)您可以解释
static_cast<D2&>(m)
和static_cast<B*>(d1)
的含义吗?
static_cast<D2&>(m)
,就像将class M
的对象视为class D2
static_cast<B*>(d1)
,就像将class D1
的指针视为class B1
。
两者都是有效的演员。
由于g()
不是virtual
函数选择发生在 compile time 。如果是 virtual
,那么所有这些铸造都不重要。
(3)您如何在这种组合中迷失方向?你在画什么吗?
当然,这很复杂,乍一看,如果有这么多这样的班级,一个人可能会很容易丢失。
(4)在普通项目中发现这样的复杂设置是否常见?
根本不是,这是不寻常的,有时是代码的气味。
这个问题实际上是多个问题:
- 当使用非
virtual
继承时,为什么virtual
函数B::f()
不覆盖?答案当然是您有两个Base
对象:一个作为D1
的基础,它覆盖f()
,一个作为D2
的基础,它不会覆盖f()
。取决于您认为对象在调用f()
时得出的对象,您将获得不同的结果。当您将设置更改为只有一个B
子对象时,请考虑继承图中的任何覆盖(如果两个分支都覆盖它,我认为您会遇到一个错误,除非您将其覆盖在分支再次合并的地方。 -
static_cast<D2&>(m)
是什么意思?由于f()
来自Base
有两个版本,因此您需要选择所需的CC_64。使用static_cast<D2&>(m)
,您将M
视为D2
对象。没有演员,编译器将无法分辨出您正在查看的两个主题中的哪一个,这会带来模棱两可的错误。 -
static_cast<B*>(d1)
是什么意思?它恰好是不必要的,但将对象视为B*
对象。
通常,我倾向于避免对任何微不足道的任何东西进行多重继承。在大多数情况下,我使用多个继承来利用空基础优化或创建具有可变数量成员的东西(想想std::tuple<...>
)。我不确定我是否曾经遇到过使用多个继承来处理生产代码中的多态性的实际需求。
1)您能解释为什么1个更改为4?
没有虚拟继承, M
中的B
有两个实例,一个用于此"钻石"的每个分支。钻石边缘之一(D2
)覆盖该功能,另一个(D1
)没有。由于d1
被称为D1
,因此d1->f()
表示您希望访问其功能 not 覆盖的B
的副本。如果要铸造到D2
,您会得到不同的结果。
通过使用虚拟继承,您将B
的两个实例合并为一个,因此D2::f
有效地覆盖B:f
CC_83。
2)您能解释
static_cast<D2&>(m)
和static_cast<B*>(d1)
的含义吗?
他们分别投放到D2&
和B*
。由于g
不是虚拟的,因此B:::g
被调用。
3)您如何在这种组合中迷失方向?你在画什么吗?
有时;)
4)在普通项目中发现这样的复杂设置是否常见?
不太常见。实际上,整个语言都可以很好地融为一体,而没有多种语言,更不用说虚拟继承了(Java,c#...)。
但是,有时候它可以使事情变得更容易,尤其是在图书馆开发中。
- C++boost序列化多态性问题
- 为什么我在虚幻引擎中的多态性和接口方面遇到问题?
- 使用多态性解决代码重复问题
- 与智能指针和矢量C 的多态性有关的问题
- std::vector 和/或多态性的问题
- 多种继承和多态性问题
- C 成员功能多态性问题
- C 的多态性问题 - 数据覆盖
- 静态多态性问题
- 我正在尝试[C++]练习多态性和OOD原则。需要指导和几个问题的答案
- C++对象多态性问题
- 涉及指针成员变量和多态性的非常奇怪的问题
- 多态性和受保护的继承问题
- 多态性确定问题
- 当独生子女类具有虚拟方法时使用多态性的问题
- 解决这个多态性问题的最优雅的方法
- 带有列表的c++多态性问题
- 实例化子类的多态性问题
- 基类和派生类构造函数多态性问题
- C++中的多态性问题