我正在尝试完成一些学校作业,我刚刚注意到在多重继承的派生类中重用基类中的函数可能会导致问题。
我们说我有这些课程:
class A
class B: public A
class C: public A
class D: public B, public C
每个班级都有这个方法:
virtual void read(ifstream& in);
在class B
和class C
中,read()
函数也在调用class A::read()
:
void B::read(ifstream& in)
{
A::read(in);
/* read method of B*/
}
void C::read(ifstream& in)
{
A::read(in);
/* read method of C*/
}
现在问题是,当我想为read()
制作class D
函数时,我实际上是在调用A::read()
两次:
void D::read(ifstream& in)
{
B::read(in);
C::read(in);
/* read method of D*/
}
我知道我可以使用仅在一个班级A::read()
或B
)中使用C
的选项,但让我们说我需要在两个班级中使用@ContentChildren(mfootComponent) children: QueryList<mfootComponent>;
类。
答案 0 :(得分:2)
这是为什么不鼓励多重继承,特别是来自共同祖先的继承的一个例子。不是因为它总是很糟糕 - 虽然它经常是! - 但更多因为它通常很难。如果你能找到另一种选择,那通常更可取。不必要。我相信你会考虑这个并决定它是否是最好的设计。但是现在,我们在这里寻找避免重复A::read()
和其他陷阱的方法。
我首先对着名的Dreaded Diamond of Doom进行了类比 - 并不像传说所暗示的那样值得恐惧,但值得记住。这说明当解决由这种菱形继承层次结构创建的“重复基础成员”问题时,通过使用虚拟继承 - 派生类现在完全负责调用其所有构造函数虚拟基地。构造函数调用不会像正常情况一样向上链接,并且数据成员初始化很奇怪。谷歌吧!
<强> N.B。如果菱形层次结构的顶级类具有任何数据成员,则应使用虚拟继承,以避免重复/引入歧义。这就是它的用途。但回到主题我把它用作函数的类比(不严格要求它)。
我们的想法是从虚拟继承对最终类的要求中获取灵感,通过以相同方式处理派生类的read()
行为来手动调用虚拟基础构造函数:通过创建每个派生类来避免重复调用'面向公众的read()
方法完全负责调用所有基础方法。这样,您不仅可以精确控制调用哪种碱基方法,还可以控制它们的顺序。
如何?我们可以将每个派生的read()
的实际工作分解为每个类中受保护的“impl
ementation”函数,并在每个 final 类中提供公共覆盖的“包装函数”。然后,包装函数将负责按照您想要的顺序调用各自的类impl
以及任何所需基数的类:
class A {
protected:
void read_impl(ifstream &in) { /* A-specific stuff */ }
public:
virtual void read(ifstream &in)
{
read_impl(in);
}
};
class B: public A { // N.B. virtual public if A has data members!
protected:
void read_impl(ifstream &in) { /* B-specific stuff */ }
public:
virtual void read(ifstream &in)
{
A::read_impl(in);
read_impl(in); // B
}
};
class C: public A {
protected:
void read_impl(ifstream &in) { /* C-specific stuff */ }
public:
virtual void read(ifstream &in)
{
A::read_impl(in);
read_impl(in); // CMy
}
};
class D: public B, public C {
protected:
void read_impl(ifstream &in) { /* D-specific stuff */ }
public:
virtual void read(ifstream &in)
{
A::read_impl(in);
B::read_impl(in); // avoids calling A again from B
C::read_impl(in); // ditto from C
read_impl(in); // D
}
};
通过这种方式,您可以完全控制每个最终课程的基础内容,以及何时,无需不必要的重复调用。就DRY而言,impl
函数意味着不会对中间类重复行为代码:每个派生的read()
中的写作都是关于它们如何协调基础行为的有用的声明性信息。您还可以在等等之间添加额外的东西。
答案 1 :(得分:0)
解决这个问题的一个非常通用的方法是使用布尔值,只需要为每个类(B&amp; C)切换一个状态,如果我应该调用我的超类的函数。 另一种解决方法就是不要在B或C中调用A的函数。