我正在尝试完成一些学校作业,并且我刚刚注意到在多重继承的派生类中重用基类中的函数可能会导致问题。
假设我有这些类(class):
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*/
}
现在的问题是,当我想为 class D
创建一个 read()
函数时,我实际上是在调用 A::read ()
两次:
void D::read(ifstream& in)
{
B::read(in);
C::read(in);
/* read method of D*/
}
我知道我可以使用仅在一个类(B
或 C
)中使用 A::read()
的选项,但假设我需要在两个类中使用它。
最佳答案
这个例子说明了为什么不鼓励多重继承,尤其是来自共同祖先的多重继承。并不是因为它总是不好——尽管它经常如此!——而是因为它通常困难。如果您能找到替代方案,通常会更好。不必要。我相信您会考虑这一点并决定这是否是最好的设计。但现在,我们在这里寻找避免重复 A::read()
和其他陷阱的方法。
我首先与著名的可怕的末日钻石进行类比 - 并不像传说中那样令人恐惧,但值得牢记。这很说明问题,当解决由这种菱形继承层次结构产生的“重复的基成员”问题时,通过使用虚拟继承 - 派生类现在完全负责调用其所有成员的所有构造函数。虚拟基地。构造函数调用不会像正常情况那样向上链接,并且数据成员初始化很奇怪。谷歌一下!
注意如果菱形层次结构的顶层类具有任何数据成员,则应该使用虚拟继承,以避免重复/引入它们的歧义。这就是它的用途。但回到主题,我用它来类比函数(并不严格要求它)。
这个想法是从虚拟继承对最终类手动调用虚拟基类的构造函数的要求中获得灵感,通过以相同的方式处理派生类的 read()
行为:避免重复调用每个派生类的面向公众的 read()
方法完全负责调用所有基类方法。这还使您不仅可以很好地控制调用哪些基础方法,还可以控制它们的顺序。
read()
的实际工作,以保护每个类中的“impl
mentation”函数,并在每个中提供公共(public)重写的“包装函数” >最后一个类。然后,包装函数将负责按照您想要的顺序调用各自类的 impl
和任何所需基类的 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()
中的写入都是关于它们如何编排的有用声明性信息基地的行为。您还可以在等等之间添加额外的内容。
关于c++ - 多重继承的派生类 : How to reuse derived functions without repeating calls to base,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38280782/