Inherit from multiple partial implementations of an abstract base class?

拟墨画扇 提交于 2019-12-03 05:44:54

Try inheriting virtually from Base:

struct D1 : virtual Base
{
    void F1() override { std::cout << __func__ << std::endl; }
};

struct D2 : virtual Base
{
    void F2() override { std::cout << __func__ << std::endl; }
};

Without the virtual inheritance, your multiple inheritance scenario looks like inheritance from two separate and incomplete base classes D1 and D2, neither of which can be instantiated.

Is it possible to have a number of partial implementations of an abstract interface, and then collect these partial implementations into a single concrete class by using multiple inheritence?

Yes.

Each Base base class subobject brings two pure virtual functions. How many of those base subobjects do you want in Deriv?

  • If you want 2 Base base class subobject, Deriv::D1::Base and Deriv::D2::Base (so conversions from Deriv& to Base& would be ambiguous) then you will have 4 different virtual functions in Deriv: Deriv::D1::Base::F1(), Deriv::D1::Base::F2(), Deriv::D2::Base::F1(), Deriv::D2::Base::F2(). Only the first and last ones are implemented, so the two middle ones are pure virtual: Deriv::D1::Base::F2(), Deriv::D2::Base::F1(). You have two entirely independent inheritance relations: Deriv inherits from D1 and Deriv inherits from D2.
  • If you want only one Base base class subobject Deriv::Base, then you will have only 2 different virtual functions in Deriv: Base::F1(), Base::F2().

Non-virtual inheritance in C++ is "concrete" inheritance, like containment: struct D : B means that for each D object there is exactly one B base class subobject, just like struct C { M m; } means that for each C there is exactly one M class member subobject. These relations are one-to-one.

OTOH, virtual inheritance is a more "abstract": struct D : virtual B means that for each D object is associated with a B base class subobject, but this relation is many-to-one, like struct C { M &m; }.

In general, for any derived class D (here Deriv), and for any virtual base B of D (here Base), all the base classes of D virtually derived from B (here Deriv::D1, Deriv::D2) contribute to the overriding of the virtual functions in the base class:

  • Base::F1() is overridden by Deriv::D1::F1()
  • Base::F2() is overridden by Deriv::D2::F2()

Non-virtual and virtual inheritance are very different inheritance relations, just like if non-virtual member functions and a virtual function introduce different relations between functions with the same signature in derived and base classes (hiding vs. overriding).

Like virtual and non-virtual functions, virtual and non-virtual base classes must be used in different situations (and one is not "better" than the other in general).

How to "fix" your code without virtual inheritance?

struct Deriv : D1, D2
{
    using D1::F1; // I added these using clauses when it first didn't compile - they don't help
    using D2::F2;
};

Yes: using declaration control name-lookup, so they affect visibility and ambiguity issues, not virtual functions overriding.

With your original non-virtual inheritance based design, in order to make Deriv a concrete class, you would have to explicitly implement both F1() and F2() virtual function signatures (there are 4 virtual functions in Deriv, but with only 2 different signatures), so you need 2 function definitions:

struct Deriv : D1, D2
{
    void F1() override { D1::F1(); }
    void F2() override { D2::F2(); }
};

Note that Deriv::F1() overrides Deriv::D1::F1() and Deriv::D2::F1().

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!